/* hp3000_cpu_base.c: HP 3000 CPU base set instruction simulator

   Copyright (c) 2016-2021, J. David Bryan

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

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

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
   AUTHOR 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 the author shall not be used
   in advertising or otherwise to promote the sale, use or other dealings in
   this Software without prior written authorization from the author.

   07-Apr-21    JDB     Modified PCN and CMD for extended Series III
   30-Sep-20    JDB     Cleaned up QWORD assembly and disassembly in "shift_48_64"
   05-Sep-20    JDB     Added EIS decoding and "cpu_eis_[fp|dec]_op" calls
   09-Dec-19    JDB     Replaced debugging macros with tracing macros
   27-Dec-18    JDB     Revised fall through comments to comply with gcc 7
   08-Jan-17    JDB     Fixed bug in SCAL 0/PCAL 0 if a stack overflow occurs
   07-Nov-16    JDB     SETR doesn't set cpu_base_changed if no register change;
                        renamed cpu_byte_to_word_ea to cpu_byte_ea
   03-Nov-16    JDB     Added zero offsets to the cpu_call_procedure calls
   24-Oct-16    JDB     Renamed SEXT macro to SEXT16
   22-Oct-16    JDB     Changed "interrupt_pending" to global for use by CIS
   07-Oct-16    JDB     Moved "extern cpu_dev" to hp3000_cpu.h where it belongs
   22-Sep-16    JDB     Moved byte_to_word_address to hp3000_cpu.c
   21-Sep-16    JDB     Added the COBOL II Extended Instruction Set dispatcher
   12-Sep-16    JDB     Use the PCN_SERIES_II and PCN_SERIES_III constants
   23-Aug-16    JDB     Implement the CMD instruction and module interrupts
   11-Jun-16    JDB     Bit mask constants are now unsigned
   13-Jan-16    JDB     First release version
   11-Dec-12    JDB     Created

   References:
     - HP 3000 Series II System Microprogram Listing
         (30000-90023, August 1976)
     - HP 3000 Series II/III System Reference Manual
         (30000-90020, July 1978)
     - Machine Instruction Set Reference Manual
         (30000-90022, June 1984)


   This module implements all of the HP 3000 Series II/III base set
   instructions, except for the memory address instructions, which are
   implemented in the main CPU module.


   Implementation notes:

    1. Each instruction executor begins with a comment listing the instruction
       mnemonic and, following in parentheses, the condition code setting, or
       "none" if the condition code is not altered, and a list of any traps that
       might be generated.  The condition code and trap mnemonics are those used
       in the Machine Instruction Set manual.

    2. In the instruction executors, "TOS" refers to the top-of-the-stack value,
       and "NOS" refers to the next-to-the-top-of-the-stack value.

    3. The order of operations in the executors follows the microcode so that
       the registers, condition code, etc. have the expected values if stack
       overflow or underflow traps occur.

    4. There is no common "cpu_div_16" routine, as each of the five base-set
       division instructions (DIVI, DIV, LDIV, DIVL, and DDIV) has a different
       overflow condition.  Therefore, they are all implemented inline.
*/



#include "hp3000_defs.h"
#include "hp3000_cpu.h"
#include "hp3000_cpu_fp.h"
#include "hp3000_cpu_ims.h"
#include "hp3000_mem.h"



/* Program constants */

#define SIO_OK              0100000u            /* TIO bit 0 = SIO OK */
#define DIO_OK              0040000u            /* TIO bit 1 = DIO OK */

#define NORM_BIT            (D48_SIGN >> 6)     /* triple normalizing examines bit 6 */
#define NORM_MASK           (D48_MASK >> 6)     /* triple normalizing masks off bits 0-5 */

#define TO_UPPERCASE(b)     ((b) & ~040u)       /* alphabetic byte upshift */


/* CPU base set global data structures */

typedef enum {                                  /* types of shifts */
    arithmetic,                                 /*   arithmetic shift */
    logical,                                    /*   logical shift */
    circular,                                   /*   circular shift (rotate) */
    normalizing                                 /*   normalizing shift */
    } SHIFT_TYPE;

typedef enum {                                  /* shift operand sizes */
    size_16,                                    /*   16-bit single word */
    size_32,                                    /*   32-bit double word */
    size_48,                                    /*   48-bit triple word */
    size_64                                     /*   64-bit quad word */
    } OPERAND_SIZE;


/* CPU base set local utility routines */

static uint32 add_32               (uint32 augend,  uint32 addend);
static uint32 sub_32               (uint32 minuend, uint32 subtrahend);
static void   shift_16_32          (HP_WORD opcode, SHIFT_TYPE shift, OPERAND_SIZE op_size);
static void   shift_48_64          (HP_WORD opcode, SHIFT_TYPE shift, OPERAND_SIZE op_size);
static void   check_stack_bounds   (HP_WORD new_value);
static uint32 tcs_io               (IO_COMMAND command);
static uint32 srw_io               (IO_COMMAND command, HP_WORD ready_flag);
static void   decrement_stack      (uint32 decrement);

static t_stat move_words           (ACCESS_CLASS source_class, uint32 source_base,
                                    ACCESS_CLASS dest_class,   uint32 dest_base,
                                    uint32 decrement);

/* CPU base set local instruction execution routines */

static t_stat move_spec          (void);
static t_stat firmware_extension (void);
static t_stat io_control         (void);



/* CPU base set global utility routines */


/* Test for a pending interrupt.

   This routine is called from within an executor for an interruptible
   instruction to test for a pending interrupt.  It counts an event tick and
   returns TRUE if the instruction should yield, either for an interrupt or for
   an event error, or FALSE if the instruction should continue.

   Instructions that potentially take a long time (e.g., MOVE, SCU, LLSH) test
   for pending interrupts after each word or byte moved or scanned.  The design
   of these instructions is such that an interrupt may be serviced and the
   instruction resumed without disruption.  For example, the MOVE instruction
   updates the source and target addresses and word count on the stack after
   each word moved.  If the instruction is interrupted, the values on the stack
   indicate where to resume after the interrupt handler completes.


   Implementation notes:

    1. The routine is essentially the same sequence as is performed at the top
       of the instruction execution loop in the "sim_instr" routine.  The
       differences are that this routine backs up P to rerun the instruction
       after the interrupt is serviced, and the interrupt holdoff test necessary
       for the SED instruction isn't done here, as this routine is not called by
       the SED executor.

    2. The event interval decrement that occurs in the main instruction loop
       after each instruction execution is cancelled here if "sim_process_event"
       returns an error code.  This is done so that a STEP command does not
       decrement sim_interval twice.  Note that skipping the initial decrement
       here does not help, as it's the sim_interval value AFTER the call to
       sim_process_event that must be preserved.
*/

t_bool cpu_interrupt_pending (t_stat *status)
{
uint32 device_number = 0;

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

if (sim_interval <= 0) {                                /* if an event timeout expired */
    *status = sim_process_event ();                     /*   then process the event service */

    if (*status != SCPE_OK) {                           /* if the service failed */
        P = P - 1 & R_MASK;                             /*   then back up to reenter the instruction */
        sim_interval = sim_interval + 1;                /*     and cancel the instruction loop increment */

        return TRUE;                                    /* abort the instruction and stop the simulator */
        }
    }

else                                                    /* otherwise */
    *status = SCPE_OK;                                  /*   indicate good status from the service */

if (sel_request)                                        /* if a selector channel request is pending */
    sel_service (1);                                    /*   then service it */

if (mpx_request_set)                                    /* if a multiplexer channel request is pending */
    mpx_service (1);                                    /*   then service it */

if (iop_interrupt_request_set && STA & STATUS_I)        /* if a hardware interrupt request is pending and enabled */
    device_number = iop_poll ();                        /*   then poll to acknowledge the request */

if (CPX1 & CPX1_IRQ_SET) {                              /* if an interrupt is pending */
    P = P - 1 & R_MASK;                                 /*   then back up to reenter the instruction */
    cpu_run_mode_interrupt (device_number);             /*     and set up the service routine */

    return TRUE;                                        /* abort the instruction */
    }

else                                                    /* otherwise */
    return FALSE;                                       /*   continue with the current instruction */
}


/* Execute a short branch.

   The program counter is adjusted by the displacement specified in the CIR, and
   the NIR is loaded with the target instruction.  If the "check_loop" parameter
   is TRUE, an infinite loop check is made if the corresponding simulator stop
   is enabled.  Branch instructions that cannot cause an infinite loop because
   they modify the CPU state during execution will specify the parameter as
   FALSE.

   On entry, the CIR must be loaded with a branch instruction having a short
   (5-bit plus sign bit) displacement.  The instruction format is:

       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   0   1 | I |   branch opcode   |+/-|  P displacement   |  Branch
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   On exit, the NIR and P registers are updated, and STOP_INFLOOP is returned if
   an infinite loop is enabled and was detected, or SCPE_OK is returned if
   simulation may continue.
*/

t_stat cpu_branch_short (t_bool check_loop)
{
HP_WORD displacement, address;
t_stat  status;

displacement = CIR & DISPL_31_MASK;                     /* get the displacement */

if (CIR & DISPL_31_SIGN)                                /* if the displacement is negative */
    address = P - 2 - displacement & LA_MASK;           /*   then subtract the displacement from the base */
else                                                    /* otherwise */
    address = P - 2 + displacement & LA_MASK;           /*   add the displacement to the base */

if ((CIR & I_FLAG_BIT_4) != 0) {                                /* if the mode is indirect */
    cpu_read_memory (program_checked, address, &displacement);  /*   then get the displacement value */

    address = address + displacement & LA_MASK;         /* add the displacement to the base */
    }

if (cpu_stop_flags & SS_LOOP                            /* if the infinite loop stop is active */
  && check_loop                                         /*   and an infinite loop is possible */
  && address == (P - 2 & LA_MASK))                      /*   and the target is the current instruction */
    status = STOP_INFLOOP;                              /*     then stop the simulator */
else                                                    /* otherwise */
    status = SCPE_OK;                                   /*   continue */

cpu_read_memory (fetch_checked, address, &NIR);         /* load the next instruction register */
P = address + 1 & R_MASK;                               /*   and increment the program counter */

return status;                                          /* return the execution status */
}


/* Add two 16-bit numbers.

   Two 16-bit values are added, and the 16-bit sum is returned.  The C (carry)
   bit in the status register is set if the result is truncated and cleared
   otherwise.  The O (overflow) bit is set if the result exceeds the maximum
   positive or negative range, i.e., the result overflows into the sign bit.  In
   addition, an integer overflow interrupt (ARITH trap) occurs if the user trap
   bit is set.
*/

HP_WORD cpu_add_16 (HP_WORD augend, HP_WORD addend)
{
uint32 sum;

sum = augend + addend;                                  /* sum the values */

SET_CARRY (sum > D16_UMAX);                             /* set C if there's a carry out of the MSB */

SET_OVERFLOW (D16_SIGN                                  /* set O if the signs */
                & (~augend ^ addend)                    /*   of the operands are the same */
                & (augend ^ sum));                      /*     but the sign of the result differs */

return (HP_WORD) LOWER_WORD (sum);                      /* return the lower 16 bits of the sum */
}


/* Subtract two 16-bit numbers.

   Two 16-bit values are subtracted, and the 16-bit difference is returned.  The
   C (carry) bit in the status register is set if the subtraction did not
   require a borrow for the most-significant bit.  The O (overflow) bit is set
   if the result exceeds the maximum positive or negative range, i.e., the
   result borrows from the sign bit.  In addition, an integer overflow interrupt
   (ARITH trap) occurs if the user trap bit is set.


   Implementation notes:

    1. The carry bit is set to the complement of the borrow, i.e., carry = 0 if
       there is a borrow and 1 is there is not.
*/

HP_WORD cpu_sub_16 (HP_WORD minuend, HP_WORD subtrahend)
{
uint32 difference;

difference = minuend - subtrahend;                      /* subtract the values */

SET_CARRY (subtrahend <= minuend);                      /* set C if no borrow from the MSB was done */

SET_OVERFLOW (D16_SIGN                                  /* set O if the signs */
               & (minuend ^ subtrahend)                 /*   of the operands differ */
               & (minuend ^ difference));               /*     as do the signs of the minuend and result */

return (HP_WORD) LOWER_WORD (difference);               /* return the lower 16 bits of the difference */
}


/* Multiply two 16-bit numbers.

   Two 16-bit values are multiplied, and the 16-bit product is returned.  The O
   (overflow) bit in the status register is set if the result exceeds the
   maximum positive or negative range, i.e., if the top 17 bits of the 32-bit
   result are not all zeros or ones.  In addition, an integer overflow interrupt
   (ARITH trap) occurs if the user trap bit is set.
*/

HP_WORD cpu_mpy_16 (HP_WORD multiplicand, HP_WORD multiplier)
{
int32  product;
uint32 check;

product = SEXT16 (multiplicand) * SEXT16 (multiplier);  /* sign-extend the operands and multiply */

check = (uint32) product & S16_OVFL_MASK;               /* check the top 17 bits and set overflow */
SET_OVERFLOW (check != 0 && check != S16_OVFL_MASK);    /*   if they are not all zeros or all ones */

return (HP_WORD) LOWER_WORD (product);                  /* return the lower 16 bits of the product */
}



/* CPU base set global instruction execution routines */


/* Execute a stack instruction (subopcode 00).

   This routine is called to execute a single stack instruction held in the CIR.
   The instruction format is:

       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   0   0 |   1st stack opcode    |   2nd stack opcode    |  Stack
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   As a single program word holds two stack opcodes, this routine is generally
   called twice.  If the R (right-hand) bit in the status register is set, the
   opcode in the lower six bits of the CIR is executed; otherwise, the opcode in
   the upper six bits is executed.  The R bit is set when the left-hand opcode
   is executing if the right-hand opcode is not a NOP.  This is an optimization
   that causes the instruction loop to fetch the next instruction in lieu of
   calling this routine again to execute the right-hand NOP.  The R bit also
   marks a pending right-hand stack opcode execution when an interrupt is
   detected after the left-hand stack opcode completes.


   Implementation notes:

    1. The entry status must be saved so that it may be restored if the
       unimplemented opcode 072 is executed with the SS_UNIMPL simulator stop
       flag set.  This allows the instruction to be reexecuted and the
       Unimplemented Instruction trap taken if the stop is subsequently
       bypassed.

    2. In hardware, the NEXT microcode order present at the end of each
       instruction transfers the NIR content to the CIR, reads the memory word
       at P into the NIR, and increments P.  However, if an interrupt is
       present, then this action is omitted, and a microcode jump is performed
       to control store location 3, which then jumps to the microcoded interrupt
       handler.  In simulation, the CIR/NIR/P update is performed before the
       next instruction is executed, rather than after the last instruction
       completes, so that interrupts are handled before updating.

       In addition, the NEXT action is modified in hardware if the NIR contains
       a stack instruction with a non-NOP B (right-hand) stack opcode.  In this
       case, NEXT transfers the NIR content to the CIR, reads the memory word at
       P into the NIR, but does not increment P.  Instead, the R bit of the
       status register is set to indicate that a B stackop is pending.  When the
       NEXT at the completion of the A (left-hand) stackop is executed, the NIR
       and CIR are untouched, but P is incremented, and the R bit is cleared.
       This ensures that if an interrupt or trap occurs between the stackops, P
       will point correctly at the next instruction to be executed.

       In simulation, following the hardware would require testing the NIR for a
       non-NOP B stackop at every pass through the instruction execution loop.
       To avoid this, the NEXT simulation unilaterally increments P, rather than
       only when a B stackop is not present, and the stack instruction executor
       tests for the B stackop and sets the R bit there.  However, by that time,
       P has already been incremented, so we decrement it here to return it to
       the correct value.

    3. Increments, decrements, and negates use the "cpu_add_16" and "cpu_sub_16"
       instead of inline adds and subtracts in order to set the carry and
       overflow status bits properly.

    4. On division by zero, the FDIV microcode sets condition code CCA before
       trapping.  All other floating-point arithmetic traps are taken before
       setting the condition code.
*/

t_stat cpu_stack_op (void)
{
static const uint8 preadjustment [64] = {       /* stack preadjustment, indexed by operation */
    0, 2, 2, 0, 0, 0, 0, 0,                     /*   NOP  DELB DDEL ZROX INCX DECX ZERO DZRO */
    4, 4, 4, 2, 3, 2, 4, 2,                     /*   DCMP DADD DSUB MPYL DIVL DNEG DXCH CMP  */
    2, 2, 2, 2, 1, 1, 2, 2,                     /*   ADD  SUB  MPY  DIV  NEG  TEST STBX DTST */
    2, 1, 2, 1, 1, 1, 1, 1,                     /*   DFLT BTST XCH  INCA DECA XAX  ADAX ADXA */
    1, 2, 2, 1, 0, 1, 2, 1,                     /*   DEL  ZROB LDXB STAX LDXA DUP  DDUP FLT  */
    4, 4, 4, 4, 4, 2, 3, 2,                     /*   FCMP FADD FSUB FMPY FDIV FNEG CAB  LCMP */
    2, 2, 2, 3, 1, 2, 2, 2,                     /*   LADD LSUB LMPY LDIV NOT  OR   XOR  AND  */
    2, 2, 0, 2, 2, 2, 2, 2                      /*   FIXR FIXT  --  INCB DECB XBX  ADBX ADXB */
    };

HP_WORD entry_status, exchanger;
uint32  operation, sum, difference, uproduct, udividend, uquotient, uremainder, check;
int32   product, dividend, divisor, quotient, remainder;
FP_OPND operand_u, operand_v, operand_w;
t_stat  status = SCPE_OK;

entry_status = STA;                                     /* save the entry status for a potential rollback */

if (STA & STATUS_R) {                                   /* if right-hand stackop is pending */
    operation = STACKOP_B (CIR);                        /*   then get the right-hand opcode */
    STA &= ~STATUS_R;                                   /*     and flip the flag off */
    }

else {                                                  /* otherwise */
    operation = STACKOP_A (CIR);                        /*   get the left-hand opcode */

    if (STACKOP_B (CIR) != NOP) {                       /* if the right-hand opcode is a not a NOP */
        STA |= STATUS_R;                                /*   then set the right-hand stackop pending flag */
        P = P - 1 & R_MASK;                             /*     and decrement P to cancel the later increment */
        }
    }

PREADJUST_SR (preadjustment [operation]);               /* preadjust the TOS registers to the required number */

switch (operation) {                                    /* dispatch the stack operation */

    case 000:                                           /* NOP (none; none)*/
        break;                                          /* there is nothing to do for a no-operation */


    case 001:                                           /* DELB (none; STUN) */
        RB = RA;                                        /* copy the TOS into the NOS */
        cpu_pop ();                                     /*   and pop the TOS, effectively deleting the NOS */
        break;


    case 002:                                           /* DDEL (none; STUN) */
        cpu_pop ();                                     /* pop the TOS */
        cpu_pop ();                                     /*   and the NOS */
        break;


    case 003:                                           /* ZROX (none; none) */
        X = 0;                                          /* set X to zero */
        break;


    case 004:                                           /* INCX (CCA, C, O; ARITH) */
        X = cpu_add_16 (X, 1);                          /* increment X */
        SET_CCA (X, 0);                                 /*   and set the condition code */
        break;


    case 005:                                           /* DECX (CCA, C, O; ARITH) */
        X = cpu_sub_16 (X, 1);                          /* decrement X */
        SET_CCA (X, 0);                                 /*   and set the condition code */
        break;


    case 006:                                           /* ZERO (none; STOV) */
        cpu_push ();                                    /* push the stack down */
        RA = 0;                                         /*   and set the TOS to zero */
        break;


    case 007:                                           /* DZRO (none; STOV) */
        cpu_push ();                                    /* push the stack */
        cpu_push ();                                    /*   down twice */
        RA = 0;                                         /* set the TOS */
        RB = 0;                                         /*   and NOS to zero */
        break;


    case 010:                                           /* DCMP (CCC; STUN) */
        SR = 0;                                         /* pop all four values from the stack */
        SET_CCC (RD, RC, RB, RA);                       /*   and set the (integer) condition code */
        break;


    case 011:                                           /* DADD (CCA, C, O; STUN, ARTIH) */
        sum = add_32 (TO_DWORD (RD, RC),                /* add the two 32-bit double words on the stack */
                      TO_DWORD (RB, RA));

        RD = UPPER_WORD (sum);                          /* split the MSW */
        RC = LOWER_WORD (sum);                          /*   and the LSW of the sum */

        cpu_pop ();                                     /* pop the old TOS */
        cpu_pop ();                                     /*   and the old NOS */

        SET_CCA (RB, RA);                               /* set the condition code */
        break;


    case 012:                                           /* DSUB (CCA, C, O; STUN, ARTIH) */
        difference = sub_32 (TO_DWORD (RD, RC),         /* subtract the two 32-bit double words on the stack */
                             TO_DWORD (RB, RA));

        RD = UPPER_WORD (difference);                   /* split the MSW */
        RC = LOWER_WORD (difference);                   /*   and the LSW of the difference */

        cpu_pop ();                                     /* pop the old TOS */
        cpu_pop ();                                     /*   and the old NOS */

        SET_CCA (RB, RA);                               /* set the condition code */
        break;


    case 013:                                           /* MPYL (CCA, C, O; STUN, ARITH) */
        product = SEXT16 (RA) * SEXT16 (RB);            /* sign-extend the 16-bit operands and multiply */

        RB = UPPER_WORD (product);                      /* split the MSW */
        RA = LOWER_WORD (product);                      /*   and the LSW of the product */

        check = (uint32) product & S16_OVFL_MASK;           /* check the top 17 bits and set carry */
        SET_CARRY (check != 0 && check != S16_OVFL_MASK);   /*   if they are not all zeros or all ones */

        STA &= ~STATUS_O;                               /* clear O as this operation cannot overflow */

        SET_CCA (RB, RA);                               /* set the condition code */
        break;


    case 014:                                           /* DIVL (CCA, O; STUN, ARITH) */
        dividend = INT32 (TO_DWORD (RC, RB));           /* convert the 32-bit dividend to a signed value */
        divisor  = SEXT16 (RA);                         /*   and sign-extend the 16-bit divisor */

        RB = RA;                                        /* delete the LSW from the stack now */
        cpu_pop ();                                     /*   to conform with the microcode */

        if (RA == 0)                                    /* if dividing by zero */
            MICRO_ABORT (trap_Integer_Zero_Divide);     /*   then trap or set the overflow flag */

        if (abs (divisor) <= abs (SEXT16 (RB)))         /* if the divisor is <= the MSW of the dividend */
            SET_OVERFLOW (TRUE);                        /*   an overflow will occur on the division */

        else {                                          /* otherwise, the divisor might be large enough */
            quotient  = dividend / divisor;             /* form the 32-bit signed quotient */
            remainder = dividend % divisor;             /*   and 32-bit signed remainder */

            check = (uint32) quotient & S16_OVFL_MASK;              /* check the top 17 bits and set overflow */
            SET_OVERFLOW (check != 0 && check != S16_OVFL_MASK);    /*   if they are not all zeros or all ones */

            RA = remainder & R_MASK;                    /* store the remainder on the TOS */
            RB = quotient  & R_MASK;                    /*   and the quotient on the NOS */

            SET_CCA (RB, 0);                            /* set the condition code */
            }
        break;


    case 015:                                           /* DNEG (CCA, O; STUN, ARITH) */
        difference = sub_32 (0, TO_DWORD (RB, RA));     /* negate the 32-bit double word on the stack */

        RB = UPPER_WORD (difference);                   /* split the MSW */
        RA = LOWER_WORD (difference);                   /*   and the LSW of the difference */

        SET_CCA (RB, RA);                               /* set the condition code */
        break;


    case 016:                                           /* DXCH (CCA; STUN) */
        exchanger = RA;                                 /* exchange */
        RA = RC;                                        /*   the TOS */
        RC = exchanger;                                 /*     and the third stack word */

        exchanger = RB;                                 /* exchange */
        RB = RD;                                        /*   the NOS */
        RD = exchanger;                                 /*     and the fourth stack word */

        SET_CCA (RB, RA);                               /* set the condition code */
        break;


    case 017:                                           /* CMP (CCC; STUN) */
        SET_CCC (RB, 0, RA, 0);                         /* set the (integer) condition code */
        cpu_pop ();                                     /*   and pop the TOS */
        cpu_pop ();                                     /*     and the NOS */
        break;


    case 020:                                           /* ADD (CCA, C, O; STUN, ARITH) */
        RB = cpu_add_16 (RB, RA);                       /* add the NOS and TOS */

        SET_CCA (RB, 0);                                /* set the condition code */
        cpu_pop ();                                     /*   and pop the old TOS */
        break;


    case 021:                                           /* SUB (CCA, C, O; STUN, ARITH) */
        RB = cpu_sub_16 (RB, RA);                       /* subtract the NOS and TOS */

        SET_CCA (RB, 0);                                /* set the condition code */
        cpu_pop ();                                     /*   and pop the old TOS */
        break;


    case 022:                                           /* MPY (CCA, O; STUN, ARITH) */
        RB = cpu_mpy_16 (RA, RB);                       /* multiply the NOS and TOS */

        SET_CCA (RB, 0);                                /* set the condition code */
        cpu_pop ();                                     /*   and pop the old TOS */
        break;


    case 023:                                           /* DIV (CCA, O; STUN, ARITH) */
        if (RA == 0)                                    /* if dividing by zero */
            MICRO_ABORT (trap_Integer_Zero_Divide);     /*   then trap or set the overflow flag */

        dividend = SEXT16 (RB);                         /* sign-extend the 16-bit dividend */
        divisor  = SEXT16 (RA);                         /*   and the 16-bit divisor */

        quotient  = dividend / divisor;                 /* form the 32-bit signed quotient */
        remainder = dividend % divisor;                 /*   and 32-bit signed remainder */

        SET_OVERFLOW (dividend == -32768 && divisor == -1); /* set overflow for -2**15 / -1 */

        RA = remainder & R_MASK;                        /* store the remainder on the TOS */
        RB = quotient  & R_MASK;                        /*   and the quotient on the NOS */

        SET_CCA (RB, 0);                                /* set the condition code */
        break;


    case 024:                                           /* NEG (CCA, C, O; STUN, ARTIH) */
        RA = cpu_sub_16 (0, RA);                        /* negate the TOS */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 025:                                           /* TEST (CCA; STUN) */
        SET_CCA (RA, 0);                                /* set the condition code */
        break;


    case 026:                                           /* STBX (CCA; STUN) */
        X = RB;                                         /* store the NOS into X */
        SET_CCA (X, 0);                                 /*   and set the condition code */
        break;


    case 027:                                           /* DTST (CCA, C; STUN) */
        SET_CCA (RB, RA);                               /* set the condition code */

        check = TO_DWORD (RB, RA) & S16_OVFL_MASK;          /* check the top 17 bits and set carry */
        SET_CARRY (check != 0 && check != S16_OVFL_MASK);   /*   if they are not all zeros or all ones */
        break;


    case 030:                                           /* DFLT (CCA; none) */
        operand_u.precision = in_d;                     /* set the operand precision to double integer */

        operand_u.words [0] = RB;                       /* load the MSW */
        operand_u.words [1] = RA;                       /*   and LSW of the operand */

        operand_v = fp_exec (fp_flt, operand_u, FP_NOP);    /* convert the integer to floating point */

        RB = operand_v.words [0];                       /* unload the MSW */
        RA = operand_v.words [1];                       /*   and the LSW of the result */

        SET_CCA (RB, RA);                               /* set the condition code */
        break;


    case 031:                                           /* BTST (CCB; STUN) */
        SET_CCB (LOWER_BYTE (RA));                      /* set the condition code */
        break;


    case 032:                                           /* XCH (CCA; STUN) */
        exchanger = RA;                                 /* exchange */
        RA = RB;                                        /*   the TOS */
        RB = exchanger;                                 /*     and the NOS */

        SET_CCA (RA, 0);                                /* set the condition code */
        break;


    case 033:                                           /* INCA (CCA, C, O; STUN, ARITH) */
        RA = cpu_add_16 (RA, 1);                        /* increment the TOS */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 034:                                           /* DECA (CCA, C, O; STUN, ARITH) */
        RA = cpu_sub_16 (RA, 1);                        /* decrement the TOS */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 035:                                           /* XAX (CCA; STUN) */
        exchanger = X;                                  /* exchange */
        X = RA;                                         /*   the TOS */
        RA = exchanger;                                 /*     and X */

        SET_CCA (RA, 0);                                /* set the condition code */
        break;


    case 036:                                           /* ADAX (CCA, C, O; STUN, ARITH) */
        X = cpu_add_16 (X, RA);                         /* add the TOS to X */
        cpu_pop ();                                     /*   and pop the TOS */

        SET_CCA (X, 0);                                 /* set the condition code */
        break;


    case 037:                                           /* ADXA (CCA, C, O; STUN, ARITH) */
        RA = cpu_add_16 (X, RA);                        /* add X to the TOS */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 040:                                           /* DEL (none; STUN) */
        cpu_pop ();                                     /* pop the TOS */
        break;


    case 041:                                           /* ZROB (none; STUN) */
        RB = 0;                                         /* set the NOS to zero */
        break;


    case 042:                                           /* LDXB (CCA; STUN) */
        RB = X;                                         /* load X into the NOS */
        SET_CCA (RB, 0);                                /*   and set the condition code */
        break;


    case 043:                                           /* STAX (CCA; STUN) */
        X = RA;                                         /* store the TOS into X */
        cpu_pop ();                                     /*   and pop the TOS */

        SET_CCA (X, 0);                                 /* set the condition code */
        break;


    case 044:                                           /* LDXA (CCA; STOV) */
        cpu_push ();                                    /* push the stack down */
        RA = X;                                         /*   and set the TOS to X */

        SET_CCA (RA, 0);                                /* set the condition code */
        break;


    case 045:                                           /* DUP (CCA; STUN, STOV) */
        cpu_push ();                                    /* push the stack down */
        RA = RB;                                        /*   and copy the old TOS to the new TOS */

        SET_CCA (RA, 0);                                /* set the condition code */
        break;


    case 046:                                           /* DDUP (CCA; STUN, STOV) */
        cpu_push ();                                    /* push the stack */
        cpu_push ();                                    /*   down twice */

        RA = RC;                                        /* copy the old TOS and NOS */
        RB = RD;                                        /*   to the new TOS and NOS */

        SET_CCA (RB, RA);                               /* set the condition code */
        break;


    case 047:                                           /* FLT (CCA; none) */
        operand_u.precision = in_s;                     /* set the operand precision to single integer */

        operand_u.words [0] = RA;                       /* load the operand */

        operand_v = fp_exec (fp_flt, operand_u, FP_NOP);    /* convert the integer to floating point */

        cpu_push ();                                    /* push the stack down */

        RB = operand_v.words [0];                       /* unload the MSW */
        RA = operand_v.words [1];                       /*   and the LSW of the result */

        SET_CCA (RB, RA);                               /* set the condition code */
        break;


    case 050:                                           /* FCMP (CCC; STUN) */
        if (RB & RD & D16_SIGN)                         /* if the operand signs are both negative */
            SET_CCC (RB, RA, RD, RC);                   /*   then swap operands and compare the magnitudes */
        else                                            /* otherwise */
            SET_CCC (RD, RC, RB, RA);                   /*   compare them as they are */

        SR = 0;                                         /* pop all four values */
        break;


    case 051:                                           /* FADD (CCA, O; STUN, ARITH) */
    case 052:                                           /* FSUB (CCA, O; STUN, ARITH) */
    case 053:                                           /* FMPY (CCA, O; STUN, ARITH) */
    case 054:                                           /* FDIV (CCA, O; STUN, ARITH) */
        operand_u.precision = fp_f;                     /* set the operand precision to single_float */
        operand_v.precision = fp_f;                     /*   and the result precision to single float */

        operand_u.words [0] = RD;                       /* load the MSW */
        operand_u.words [1] = RC;                       /*   and LSW of the first operand */

        operand_v.words [0] = RB;                       /* load the MSW */
        operand_v.words [1] = RA;                       /*   and LSW of the second operand */

        STA &= ~STATUS_O;                               /* clear the overflow flag */

        cpu_pop ();                                     /* delete two words */
        cpu_pop ();                                     /*   from the stack */

        operand_w =                                         /* call the floating-point executor */
           fp_exec ((FP_OPR) (operation - 051 + fp_add),    /*   and convert the opcode */
                    operand_u, operand_v);                  /*     to an arithmetic operation */

        RB = operand_w.words [0];                       /* unload the MSW */
        RA = operand_w.words [1];                       /*   and the LSW of the result */

        if (operand_w.trap != trap_None) {                  /* if an error occurred */
            if (operand_w.trap == trap_Float_Zero_Divide)   /*   then if it is division by zero */
                SET_CCA (RB, RA);                           /*     then set the condition code */

            MICRO_ABORT (operand_w.trap);               /* trap or set overflow */
            }

        SET_CCA (RB, RA);                               /* set the condition code */
        break;


    case 055:                                           /* FNEG (CCA; STUN) */
        if ((RB | RA) == 0)                             /* if the floating point value is zero */
            SET_CCE;                                    /*   then it remains zero after negation */

        else {                                          /* otherwise */
            RB = RB ^ D16_SIGN;                         /*   flip the sign bit */
            SET_CCA (RB, 1);                            /*     and set CCL or CCG from the sign bit */
            }
        break;


    case 056:                                           /* CAB (CCA; STUN) */
        exchanger = RC;                                 /* rotate */
        RC = RB;                                        /*   the TOS */
        RB = RA;                                        /*     the NOS */
        RA = exchanger;                                 /*       and the third stack word */

        SET_CCA (RA, 0);                                /* set the condition code */
        break;


    case 057:                                           /* LCMP (CCC; STUN) */
        SET_CCC (0, RB, 0, RA);                         /* set the (logical) condition code */

        cpu_pop ();                                     /* pop the TOS */
        cpu_pop ();                                     /*   and the NOS */
        break;


    case 060:                                           /* LADD (CCA, C; STUN) */
        sum = RB + RA;                                  /* add the values */

        SET_CARRY (sum > D16_UMAX);                     /* set C if there's a carry out of the MSB */

        RB = sum & R_MASK;                              /* store the sum in the NOS */
        cpu_pop ();                                     /*   and pop the TOS */

        SET_CCA (RA, 0);                                /* set the (integer) condition code */
        break;


    case 061:                                           /* LSUB (CCA, C; STUN) */
        SET_CARRY (RA <= RB);                           /* set C if there will not be a borrow by the MSB */

        RB = RB - RA & R_MASK;                          /* subtract the values */
        cpu_pop ();                                     /*   and pop the TOS */

        SET_CCA (RA, 0);                                /* set the (integer) condition code */
        break;


    case 062:                                           /* LMPY (CCA, C; STUN) */
        uproduct = RB * RA;                             /* multiply the operands */

        RA = LOWER_WORD (uproduct);                     /* split the MSW */
        RB = UPPER_WORD (uproduct);                     /*   and the LSW of the product */

        SET_CARRY (RB > 0);                             /* set C if the product doesn't fit in one word */

        SET_CCA (RB, RA);                               /* set the (integer) condition code */
        break;


    case 063:                                           /* LDIV (CCA, O; STUN, ARITH) */
        if (RA == 0)                                    /* if dividing by zero */
            MICRO_ABORT (trap_Integer_Zero_Divide);     /*   then trap or set the overflow flag */

        udividend = TO_DWORD (RC, RB);                  /* form the 32-bit unsigned dividend */

        uquotient  = udividend / RA;                    /* form the 32-bit unsigned quotient */
        uremainder = udividend % RA;                    /*   and 32-bit unsigned remainder */

        SET_OVERFLOW (uquotient & ~D16_MASK);           /* set O if the quotient needs more than 16 bits */

        cpu_pop ();                                     /* pop the TOS */

        RA = LOWER_WORD (uremainder);                   /* store the remainder on the TOS */
        RB = LOWER_WORD (uquotient);                    /*   and the quotient on the NOS */

        SET_CCA (RB, 0);                                /* set the (integer) condition code */
        break;


    case 064:                                           /* NOT (CCA; STUN) */
        RA = ~RA & R_MASK;                              /* complement the TOS */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 065:                                           /* OR (CCA; STUN) */
        RB = RA | RB;                                   /* logically OR the TOS and NOS */

        SET_CCA (RB, 0);                                /* set the condition code */
        cpu_pop ();                                     /*   and pop the TOS */
        break;


    case 066:                                           /* XOR (CCA; STUN) */
        RB = RA ^ RB;                                   /* logically XOR the TOS and NOS */

        SET_CCA (RB, 0);                                /* set the condition code */
        cpu_pop ();                                     /*   and pop the TOS */
        break;


    case 067:                                           /* AND (CCA; STUN) */
        RB = RA & RB;                                   /* logically AND the TOS and NOS */

        SET_CCA (RB, 0);                                /* set the condition code */
        cpu_pop ();                                     /*   and pop the TOS */
        break;


    case 070:                                           /* FIXR (CCA, C, O; STUN, ARITH) */
    case 071:                                           /* FIXT (CCA, C, O; STUN, ARITH) */
        operand_u.precision = fp_f;                     /* set the operand precision to single_float */

        operand_u.words [0] = RB;                       /* load the MSW */
        operand_u.words [1] = RA;                       /*   and LSW of the operand */

        STA &= ~(STATUS_C | STATUS_O);                  /* the microcode clears the carry and overflow flags here */

        operand_v =                                         /* call the floating-point executor */
           fp_exec ((FP_OPR) (operation - 070 + fp_fixr),   /*   and convert the opcode */
                    operand_u, FP_NOP);                     /*     to a fix operation */

        if (operand_v.trap != trap_None) {              /* if an overflow occurs */
            RB = RB & FRACTION_BITS | ASSUMED_BIT;      /*   then the microcode masks and restores */
            MICRO_ABORT (operand_v.trap);               /*     the leading 1 to the mantissa before trapping */
            }

        RB = operand_v.words [0];                       /* unload the MSW */
        RA = operand_v.words [1];                       /*   and the LSW of the result */

        check = TO_DWORD (RB, RA) & S16_OVFL_MASK;          /* check the top 17 bits and set carry */
        SET_CARRY (check != 0 && check != S16_OVFL_MASK);   /*   if they are not all zeros or all ones */

        SET_CCA (RB, RA);                               /* set the condition code */
        break;


    case 072:                                           /* unimplemented */
        status = STOP_UNIMPL;                           /* report that the instruction was not executed */
        STA = entry_status;                             /*   and restore the status register entry value */
        break;


    case 073:                                           /* INCB (CCA, C, O; STUN, ARITH) */
        RB = cpu_add_16 (RB, 1);                        /* increment the NOS */
        SET_CCA (RB, 0);                                /*   and set the condition code */
        break;


    case 074:                                           /* DECB (CCA, C, O; STUN, ARITH) */
        RB = cpu_sub_16 (RB, 1);                        /* decrement the NOS */
        SET_CCA (RB, 0);                                /*   and set the condition code */
        break;


    case 075:                                           /* XBX (none; STUN) */
        exchanger = X;                                  /* exchange */
        X = RB;                                         /*   the NOS */
        RB = exchanger;                                 /*     and X */
        break;


    case 076:                                           /* ADBX (CCA, C, O; STUN, ARITH) */
        X = cpu_add_16 (X, RB);                         /* add the NOS to X */
        SET_CCA (X, 0);                                 /*   and set the condition code */
        break;


    case 077:                                           /* ADXB (CCA, C, O; STUN, ARITH) */
        RB = cpu_add_16 (X, RB);                        /* add X to the NOS */
        SET_CCA (RB, 0);                                /*   and set the condition code */
        break;

     }                                                  /* all cases are handled  */

return status;                                          /* return the execution status */
}


/* Execute a shift, branch, or bit test instruction (subopcode 01).

   This routine is called to execute the shift, branch, or bit test instruction
   currently in the CIR.  The instruction formats are:

       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   0   1 | X |   shift opcode    |      shift count      |  Shift
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   0   1 | I |   branch opcode   |+/-|  P displacement   |  Branch
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   0   1 | X |  bit test opcode  |     bit position      |  Bit Test
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+


   Implementation notes:

    1. The BCY, BNCY, BOV, and BNOV instructions will enter infinite loops if
       their displacements are zero, so they call the "cpu_branch_short" routine
       with loop checking enabled.  The other branch instructions will not enter
       an infinite loop, even with zero displacements, as they modify registers
       or the stack during execution, so they call the routine with loop
       checking disabled.

    2. All of the shift instructions except QASL and QASR use bit 9 to indicate
       a left (0) or right (1) shift and bit 4 to indicate that the shift count
       includes the index register value.  Bit 9 is always on for QASL and QASR,
       which use bit 4 to indicate a left or right shift, and which always
       include the index register value.  To simplify handling in the shifting
       routine, the QASL and QASR executors move the left/right indication to
       bit 9 and set bit 4 on before calling.
*/

t_stat cpu_shift_branch_bit_op (void)
{
static const uint8 preadjustment [32] = {       /* stack preadjustment, indexed by operation */
    1, 1, 1, 1, 1, 1, 1, 1,                     /*   ASL  ASR  LSL  LSR  CSL  CSR  SCAN IABZ    */
    3, 3, 0, 0, 0, 0, 3, 4,                     /*   TASL TASR IXBZ DXBZ BCY  BNCY TNSL QAS(LR) */
    2, 2, 2, 2, 2, 2, 2, 1,                     /*   DASL DASR DLSL DLSR DCSL DCSR CPRB DABZ    */
    0, 0, 1, 1, 1, 1, 1, 1                      /*   BOV  BNOV TBC  TRBC TSBC TCBC BRO  BRE     */
    };

HP_WORD opcode;
uint32  operation, bit_position, bit_mask, count;
t_stat  status = SCPE_OK;

operation = SBBOP (CIR);                                /* get the opcode from the instruction */

PREADJUST_SR (preadjustment [operation]);               /* preadjust the TOS registers to the required number */

switch (operation) {                                    /* dispatch the shift/branch/bit operation */

    case 000:                                           /* ASL (CCA; STUN) */
    case 001:                                           /* ASR (CCA; STUN) */
        shift_16_32 (CIR, arithmetic, size_16);         /* do an arithmetic left or right shift */
        break;


    case 002:                                           /* LSL (CCA; STUN) */
    case 003:                                           /* LSR (CCA; STUN) */
        shift_16_32 (CIR, logical, size_16);            /* do a logical left or right shift */
        break;


    case 004:                                           /* CSL (CCA; STUN) */
    case 005:                                           /* CSR (CCA; STUN) */
        shift_16_32 (CIR, circular, size_16);           /* do a circular left or right shift */
        break;


    case 006:                                           /* SCAN (CCA; STUN) */
        if (RA == 0)                                    /* if the TOS is zero */
            if (CIR & X_FLAG)                           /*   then if the instruction is indexed */
                X = X + 16 & R_MASK;                    /*     then add 16 to the index register value */
            else                                        /*   otherwise */
                X = 16;                                 /*     set the index register value to 16 */

        else {                                          /* otherwise the TOS is not zero */
            count = 0;                                  /*   so set up to scan for the first "one" bit */

            while ((RA & D16_SIGN) == 0) {              /* while the MSB is clear */
                RA = RA << 1;                           /*   shift the TOS left */
                count = count + 1;                      /*     while counting the shifts */
                }

            if (CIR & X_FLAG)                           /* if the instruction is indexed */
                X = X + count + 1 & R_MASK;             /*   then return the count + 1 */
            else                                        /* otherwise */
                X = count;                              /*   return the count */

            RA = RA << 1 & R_MASK;                      /* shift the leading "one" bit out of the TOS */
            }

        SET_CCA (RA, 0);                                /* set the condition code */
        break;


    case 007:                                           /* IABZ (CCA, C, O; STUN, BNDV) */
        RA = cpu_add_16 (RA, 1);                        /* increment the TOS */
        SET_CCA (RA, 0);                                /*   and set the condition code */

        if (RA == 0)                                    /* if the TOS is now zero */
            status = cpu_branch_short (FALSE);          /*   then branch to the target address */
        break;


    case 010:                                           /* TASL (CCA; STUN) */
    case 011:                                           /* TASR (CCA; STUN) */
        shift_48_64 (CIR, arithmetic, size_48);         /* do a triple arithmetic left or right shift */
        break;


    case 012:                                           /* IXBZ (CCA, C, O; BNDV) */
        X = cpu_add_16 (X, 1);                          /* increment X */
        SET_CCA (X, 0);                                 /*   and set the condition code */

        if (X == 0)                                     /* if X is now zero */
            status = cpu_branch_short (FALSE);          /*   then branch to the target address */
        break;


    case 013:                                           /* DXBZ (CCA, C, O; BNDV) */
        X = cpu_sub_16 (X, 1);                          /* decrement X */
        SET_CCA (X, 0);                                 /*   and set the condition code */

        if (X == 0)                                     /* if X is now zero */
            status = cpu_branch_short (FALSE);          /*   then branch to the target address */
        break;


    case 014:                                           /* BCY (C = 0; BNDV) */
        if (STA & STATUS_C) {                           /* if the carry bit is set */
            STA &= ~STATUS_C;                           /*   then clear it */
            status = cpu_branch_short (TRUE);           /*     and branch to the target address */
            }
        break;


    case 015:                                           /* BNCY (C = 0; BNDV) */
        if (STA & STATUS_C)                             /* if the carry bit is set */
            STA &= ~STATUS_C;                           /*   then clear it and do not branch */
        else                                            /* otherwise the carry bit is clear */
            status = cpu_branch_short (TRUE);           /*   so branch to the target address */
        break;


    case 016:                                           /* TNSL (CCA; STUN) */
        shift_48_64 (CIR, normalizing, size_48);        /* do a triple normalizing left shift */
        break;


    case 017:                                           /* QASL (CCA; STUN), QASR (CCA; STUN) */
        if ((CIR & ~SHIFT_COUNT_MASK) == QASR)          /* transfer the left/right flag */
            opcode = CIR | SHIFT_RIGHT_FLAG | X_FLAG;   /*   to the same position */
        else                                            /*     as the other shift instructions use */
            opcode = CIR & ~SHIFT_RIGHT_FLAG | X_FLAG;  /*       and set the indexed flag on */

        shift_48_64 (opcode, arithmetic, size_64);      /* do a quadruple arithmetic left or right shift */
        break;


    case 020:                                           /* DASL (CCA; STUN) */
    case 021:                                           /* DASR (CCA; STUN) */
        shift_16_32 (CIR, arithmetic, size_32);         /* do a double arithmetic left or right shift */
        break;


    case 022:                                           /* DLSL (CCA; STUN) */
    case 023:                                           /* DLSR (CCA; STUN) */
        shift_16_32 (CIR, logical, size_32);            /* do a double logical left or right shift */
        break;


    case 024:                                           /* DCSL (CCA; STUN) */
    case 025:                                           /* DCSR (CCA; STUN) */
        shift_16_32 (CIR, circular, size_32);           /* do a double circular left or right shift */
        break;


    case 026:                                           /* CPRB (CCE, CCL, CCG; STUN, BNDV) */
        if (SEXT16 (X) < SEXT16 (RB))                   /* if X is less than the lower bound */
            SET_CCL;                                    /*   then set CCL and continue */

        else if (SEXT16 (X) > SEXT16 (RA))              /* otherwise if X is greater than the upper bound */
            SET_CCG;                                    /*   then set CCG and continue */

        else {                                          /* otherwise lower bound <= X <= upper bound */
            SET_CCE;                                    /*   so set CCE */
            status = cpu_branch_short (FALSE);          /*     and branch to the target address */
            }

        cpu_pop ();                                     /* pop the TOS */
        cpu_pop ();                                     /*   and the NOS */
        break;


    case 027:                                           /* DABZ (CCA, C, O; STUN, BNDV) */
        RA = cpu_sub_16 (RA, 1);                        /* decrement the TOS */
        SET_CCA (RA, 0);                                /*   and set the condition code */

        if (RA == 0)                                    /* if the TOS is now zero */
            status = cpu_branch_short (FALSE);          /*   then branch to the target address */
        break;


    case 030:                                           /* BOV (O = 0; BNDV) */
        if (STA & STATUS_O) {                           /* if the overflow bit is set */
            STA &= ~STATUS_O;                           /*   then clear it */
            status = cpu_branch_short (TRUE);           /*     and branch to the target address */
            }
        break;


    case 031:                                           /* BNOV (O = 0; BNDV) */
        if (STA & STATUS_O)                             /* if the overflow bit is set */
            STA &= ~STATUS_O;                           /*   then clear it and do not branch */
        else                                            /* otherwise the overflow bit is clear */
            status = cpu_branch_short (TRUE);           /*   so branch to the target address */
        break;


    case 032:                                           /* TBC (CCA; STUN) */
    case 033:                                           /* TRBC (CCA; STUN) */
    case 034:                                           /* TSBC (CCA; STUN) */
    case 035:                                           /* TCBC (CCA; STUN) */
        bit_position = BIT_POSITION (CIR);              /* get the position of the bit to test */

        if (CIR & X_FLAG)                               /* if the instruction is indexed */
            bit_position = bit_position + X;            /*   then add the index register value */

        bit_mask = D16_SIGN >> bit_position % D16_WIDTH;    /* shift the bit mask to the desired location */

        SET_CCA (RA & bit_mask, 0);                     /* set the condition code */

        if (operation == 033)                           /* if the instruction is TRBC */
            RA = RA & ~bit_mask;                        /*   then reset the bit */

        else if (operation == 034)                      /* otherwise if the instruction is TSBC */
            RA = RA | bit_mask;                         /*   then set the bit */

        else if (operation == 035)                      /* otherwise if the instruction is TCBC */
            RA = RA ^ bit_mask;                         /*   then complement the bit */
        break;                                          /* or leave it alone for TBC */


    case 036:                                           /* BRO (none; STUN, BNDV) */
        if ((RA & 1) == 1)                              /* if the TOS is odd */
            status = cpu_branch_short (FALSE);          /*   then branch to the target address */

        cpu_pop ();                                     /* pop the TOS */
        break;


    case 037:                                           /* BRE (none; STUN, BNDV) */
        if ((RA & 1) == 0)                              /* if the TOS is even */
            status = cpu_branch_short (FALSE);          /*   then branch to the target address */

        cpu_pop ();                                     /* pop the TOS */
        break;

     }                                                  /* all cases are handled  */

return status;                                          /* return the execution status */
}


/* Execute a move, special, firmware, immediate, field, or register instruction (subopcode 02).

   This routine is called to execute the move, special, firmware, immediate,
   field, or register instruction currently in the CIR.  The instruction formats
   are:

       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   0 |  move op  | opts/S decrement  |  Move
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   0 |  special op   | 0   0 | sp op |  Special
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 |      firmware option op       |  Firmware
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 |  imm opcode   |       immediate operand       |  Immediate
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | field opcode  |    J field    |    K field    |  Field
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 |  register op  | SK| DB| DL| Z |STA| X | Q | S |  Register
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+


   Implementation notes:

    1. The PSHR and SETR instructions follow the stack usage in the microcode
       so that SR contains the same value at the end of the instruction as in
       the hardware.  The sequence of stack flushes and queue-ups is therefore
       somewhat asymmetric.

    2. The microcode for the EXF and DPF instructions calculate the alignment
       shifts as 16 - (J + K) MOD 16 and then perform circular right and left
       shifts, respectively, to align the fields.  In simulation, the alignments
       are calculated as (J + K) MOD 16, and the opposite shifts (left and
       right, respectively) are employed.  This produces the same result, as a
       circular left shift of N bits is identical to a circular right shift of
       16 - N bits.
*/

t_stat cpu_move_spec_fw_imm_field_reg_op (void)
{
static const uint8 preadjustment [16] = {       /* stack preadjustment, indexed by operation */
    0, 4, 0, 0, 1, 1, 1, 1,                     /*   ---- ---- LDI  LDXI CMPI ADDI SUBI MPYI */
    1, 0, 0, 0, 1, 1, 2, 4                      /*   DIVI PSHR LDNI LDXN CMPN EXF  DPF  SETR */
    };

int32   divisor;
uint32  operation;
HP_WORD new_sbank, new_sm, new_q, start_bit, bit_count, bit_shift, bit_mask;
t_stat  status = SCPE_OK;

operation = MSFIFROP (CIR);                             /* get the opcode from the instruction */

PREADJUST_SR (preadjustment [operation]);               /* preadjust the TOS registers to the required number */

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

    case 000:
        status = move_spec ();                          /* execute the move or special instruction */
        break;


    case 001:
        status = firmware_extension ();                 /* execute the DMUL, DDIV, or firmware extension instruction */
        break;


    case 002:                                           /* LDI (CCA; STOV) */
        cpu_push ();                                    /* push the stack down */
        RA = CIR & IMMED_MASK;                          /* store the immediate value on the TOS */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 003:                                           /* LDXI (none; none) */
        X = CIR & IMMED_MASK;                           /* load the immediate value into X */
        break;


    case 004:                                           /* CMPI (CCC; STUN) */
        SET_CCC (RA, 0, CIR & IMMED_MASK, 0);           /* set the condition code */
        cpu_pop ();                                     /*   and pop the TOS */
        break;


    case 005:                                           /* ADDI (CCA, C, O; STUN, ARITH) */
        RA = cpu_add_16 (RA, CIR & IMMED_MASK);         /* sum the TOS and the immediate value */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 006:                                           /* SUBI (CCA, C, O; STUN, ARITH) */
        RA = cpu_sub_16 (RA, CIR & IMMED_MASK);         /* difference the TOS and the immediate value */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 007:                                           /* MPYI (CCA, O; STUN, STOV, ARITH) */
        cpu_push ();                                    /* the microcode does this for commonality with */
        cpu_pop ();                                     /*   MPY and MPYM, so we must too to get STOV */

        RA = cpu_mpy_16 (RA, CIR & IMMED_MASK);         /* multiply the TOS and the immediate value */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 010:                                           /* DIVI (CCA; STUN, ARITH) */
        divisor = (int32) CIR & IMMED_MASK;             /* get the immediate (positive) divisor */

        if (divisor == 0)                               /* if dividing by zero */
            MICRO_ABORT (trap_Integer_Zero_Divide);     /*   then trap or set the overflow flag */

        RA = SEXT16 (RA) / divisor & R_MASK;            /* store the quotient (which cannot overflow) on the TOS */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 011:                                           /* PSHR (none; STOV, MODE) */
        cpu_flush ();                                   /* flush the TOS register file */

        if (SM + 9 > Z)                                 /* check the stack for enough space */
            MICRO_ABORT (trap_Stack_Overflow);          /*   before pushing any of the registers */

        if (CIR & PSR_S) {                              /* if S is to be stored */
            cpu_push ();                                /*   then push the stack down */
            RA = SM - DB & R_MASK;                      /*     and store delta S on the TOS */
            }

        if (CIR & PSR_Q) {                              /* if Q is to be stored */
            cpu_push ();                                /*   then push the stack down */
            RA = Q - DB & R_MASK;                       /*     and store delta Q on the TOS */
            }

        if (CIR & PSR_X) {                              /* if X is to be stored */
            cpu_push ();                                /*   then push the stack down */
            RA = X;                                     /*     and store X on the TOS */
            }

        if (CIR & PSR_STA) {                            /* if STA is to be stored */
            cpu_push ();                                /*   then push the stack down */
            RA = STA;                                   /*     and store the status register on the TOS */
            cpu_flush ();                               /* flush the TOS register queue */
            }

        if (CIR & PSR_Z) {                              /* if Z is to be stored */
            cpu_push ();                                /*   then push the stack down */
            RA = Z - DB & R_MASK;                       /*     and store delta Z on the TOS */
            }

        cpu_flush ();                                   /* flush the TOS register queue */

        if (CIR & PSR_DL) {                             /* if DL is to be stored */
            cpu_push ();                                /*   then push the stack down */
            RA = DL - DB & R_MASK;                      /*     and store delta DL on the TOS */
            }

        if (CIR & (PSR_DB_DBANK | PSR_SBANK)) {         /* if a bank register is to be stored */
            if (NPRV)                                   /*   then if the mode is not privileged */
                MICRO_ABORT (trap_Privilege_Violation); /*     then abort with a privilege violation */

            if (CIR & PSR_DB_DBANK) {                   /* if DBANK and DB are to be stored */
                cpu_push ();                            /*   then push the stack */
                cpu_push ();                            /*     down twice */
                RA = DB;                                /*       and store DB on the TOS */
                RB = DBANK;                             /*         and DBANK in the NOS */
                }

            if (CIR & PSR_SBANK) {                      /* if SBANK is to be stored */
                cpu_push ();                            /*   then push the stack down */
                RA = SBANK;                             /*     and store SBANK on the TOS */
                }
            }
        break;


    case 012:                                           /* LDNI (CCA; STOV) */
        cpu_push ();                                    /* push the stack down */
        RA = NEG16 (CIR & IMMED_MASK);                  /*   and store the negated immediate value on the TOS */

        SET_CCA (RA, 0);                                /* set the condition code */
        break;


    case 013:                                           /* LDXN (none; none) */
        X = NEG16 (CIR & IMMED_MASK);                   /* store the negated immediate value into X */
        break;


    case 014:                                           /* CMPN (CCC; STUN) */
        SET_CCC (RA, 0, NEG16 (CIR & IMMED_MASK), 0);   /* set the condition code */
        cpu_pop ();                                     /*   and pop the TOS */
        break;


    case 015:                                           /* EXF (CCA; STUN) */
        start_bit = START_BIT (CIR);                    /* get the starting bit number */
        bit_count = BIT_COUNT (CIR);                    /*   and the number of bits */

        bit_shift = (start_bit + bit_count) % D16_WIDTH;    /* calculate the alignment shift */

        bit_mask = (1 << bit_count) - 1;                    /* form a right-justified mask */

        RA = (RA << bit_shift | RA >> D16_WIDTH - bit_shift)    /* rotate the TOS to align with the mask */
               & bit_mask;                                      /*   and then mask to the desired field */

        SET_CCA (RA, 0);                                   /* set the condition code */
        break;


    case 016:                                           /* DPF (CCA; STUN) */
        start_bit = START_BIT (CIR);                    /* get the starting bit number */
        bit_count = BIT_COUNT (CIR);                    /*   and the number of bits */

        bit_shift = (start_bit + bit_count) % D16_WIDTH;    /* calculate the alignment shift */

        bit_mask = (1 << bit_count) - 1;                    /* form a right-justified mask */

        bit_mask = bit_mask >> bit_shift                    /* rotate it into the correct position */
                     | bit_mask << D16_WIDTH - bit_shift;   /*   to mask the target field */

        RB = (RB & ~bit_mask                                /* mask the NOS and rotate and mask the TOS to fill */
               | (RA >> bit_shift | RA << D16_WIDTH - bit_shift) & bit_mask)
               & R_MASK;

        cpu_pop ();                                     /* pop the TOS */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 017:                                           /* SETR (none; STUN, STOV, MODE)*/
        new_sbank = 0;                                  /* quell erroneous uninitialized use warning */

        if (CIR & PSR_PRIV) {                           /* setting SBANK, DB, DL, and Z are privileged */
            if (NPRV)                                   /* if the mode is not privileged */
                MICRO_ABORT (trap_Privilege_Violation); /*   then abort with a privilege violation */

            if (CIR & PSR_SBANK) {                      /* if SBANK is to be set */
                new_sbank = RA;                         /*   then change it after the parameters are retrieved */
                cpu_pop ();                             /* pop the parameter */
                }

            if (CIR & PSR_DB_DBANK) {                   /* if DBANK and DB are to be set */
                DB = RA;                                /*   then set the */
                DBANK = RB & BA_MASK;                   /*     new values */
                cpu_pop ();                             /*       and pop the */
                cpu_pop ();                             /*         parameters */
                }

            if (CIR & PSR_DL) {                         /* if DL is to be set */
                DL = RA + DB & R_MASK;                  /*   then set the new value as an offset from DB */
                cpu_pop ();                             /*     and pop the parameter */
                }

            if (SR == 0)                                /* queue up a parameter */
                cpu_queue_up ();                        /*   if it is needed */

            if (CIR & PSR_Z) {                          /* if Z is to be set */
                Z = RA + DB & R_MASK;                   /*   then set the new value as an offset from DB */
                cpu_pop ();                             /*     and pop the parameter */
                }
                                                        /* queue up another parameter */
            if (SR == 0)                                /*   if it is needed */
                cpu_queue_up ();
            }

        if (CIR & PSR_STA) {                                    /* if STA is to be set */
            if (NPRV)                                           /*   then if the mode is not privileged */
                STA = STA & ~STATUS_NPRV | RA & STATUS_NPRV;    /*     then only T, O, C, and CC can be set */
            else                                                /*   otherwise privileged mode */
                STA = RA;                                       /*     allows the entire word to be set */

            if ((STA & STATUS_OVTRAP) == STATUS_OVTRAP) /* if overflow was set with trap enabled */
                CPX1 |= cpx1_INTOVFL;                   /*   then an interrupt occurs */

            cpu_pop ();                                 /* pop the parameter */

            if (SR == 0)                                /* queue up another parameter */
                cpu_queue_up ();                        /*   if it is needed */
            }

        if (CIR & PSR_X) {                              /* if X is to be set */
            X = RA;                                     /*   then set the new value */
            cpu_pop ();                                 /*     and pop the parameter */
            }

        if (CIR & PSR_Q) {                              /* if Q is to be set */
            if (SR == 0)                                /*   then queue up another parameter */
                cpu_queue_up ();                        /*     if it is needed */

            new_q = RA + DB & R_MASK;                   /* set the new value as an offset from DB */

            check_stack_bounds (new_q);                 /* trap if the new value is outside of the stack */

            Q = new_q;                                  /* set the new value */
            cpu_pop ();                                 /*   and pop the parameter */
            }

        if (CIR & PSR_S) {                              /* if S is to be set */
            if (SR == 0)                                /*   then queue up another parameter */
                cpu_queue_up ();                        /*     if it is needed */

            new_sm = RA + DB & R_MASK;                  /* set the new value as an offset from DB */

            check_stack_bounds (new_sm);                /* trap if the new value is outside of the stack */

            cpu_flush ();                               /* flush the TOS register file */
            SM = new_sm;                                /*   and set the new stack pointer value */
            }

        if (CIR & PSR_SBANK)                            /* if SBANK is to be set */
            SBANK = new_sbank & BA_MASK;                /*   then update the new value now */

        cpu_base_changed = (CIR != SETR && CIR != SETR_X);  /* set the flag if the base registers changed */
        break;
     }                                                  /* all cases are handled  */

return status;                                          /* return the execution status */
}


/* Execute an I/O, control, program, immediate, or memory instruction (subopcode 03).

   This routine is called to execute the I/O, control, program, immediate, or
   memory instruction currently in the CIR.  The instruction formats are:

       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   1 |  program op   |            N field            |  Program
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   1 | immediate op  |       immediate operand       |  Immediate
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   1 |   memory op   |        P displacement         |  Memory
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   The "N field" of the program instructions contains an index that is used to
   locate the "program label" that describes the procedure or subroutine to call
   or exit.  Labels have this format:

       0 | 1   2   3 | 4   5   6 | 7   8   9 |10  11  12 |13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0 | U |                        address                        |  Local
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | M |        STT number         |        segment number         |  External
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Where:

     U (uncallable) = the procedure is callable from privileged mode only
     M (mapped)     = the segment number is physically mapped
     address        = the PB-relative address of the procedure entry
     STT number     = the Segment Transfer Table entry within the target segment
     segment number = the number of the target segment

   The label is located either on the top of the stack (N = 0) or by indexing
   into the STT of the current code segment (N > 0).  Labels may be either
   local, indicating a transfer within the current segment, or external,
   indicating a transfer to another segment.


   Implementation notes:

    1. In hardware, the LDPP and LDPN microcode performs the bounds test E >= PB
       on the effective address E, then does a queue down if necessary, then
       performs the bounds test E < PL (instead of <= to account for second
       word), and then does another queue down if necessary, before reading the
       two words and storing them in the RA and RB registers.  Therefore, the
       order of possible traps is BNDV, STOV, BNDV, and STOV.

       In simulation, the "cpu_read_memory" routine normally checks the upper
       and lower bounds together.  This would lead to a trap order of BNDV,
       BNDV, STOV, and STOV.  To implement the microcode order, explicit bounds
       checks are interleaved with the stack pushes, and then unchecked reads
       are done to obtain the operands.
*/

t_stat cpu_io_cntl_prog_imm_mem_op (void)
{
static const uint8 preadjustment [16] = {       /* stack preadjustment, indexed by operation */
    0, 0, 0, 0, 1, 0, 0, 0,                     /*   ---- SCAL PCAL EXIT SXIT ADXI SBXI LLBL */
    0, 0, 1, 1, 0, 1, 1, 1                      /*   LDPP LDPN ADDS SUBS ---- ORI  XORI ANDI */
    };

ACCESS_CLASS class;
uint32       operation;
HP_WORD      field, operand, offset, new_p, new_q, new_sm, stt_length, label;
t_stat       status = SCPE_OK;

field = CIR & DISPL_255_MASK;                           /* get the N/immediate/displacement field value */

operation = IOCPIMOP (CIR);                             /* get the opcode from the instruction */

PREADJUST_SR (preadjustment [operation]);               /* preadjust the TOS registers to the required number */

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

    case 000:
        status = io_control ();                         /* execute the I/O or control instruction */
        break;


    case 001:                                           /* SCAL (none; STOV, STUN, STTV, BNDV) */
        if (field == 0) {                               /* if the label is on the TOS */
            PREADJUST_SR (1);                           /*   then ensure a valid TOS register */
            label = RA;                                 /*     before getting the label */
            cpu_pop ();                                 /*       and popping it from the stack */
            }

        else                                                /* otherwise, the label is at M [PL-N] */
            cpu_read_memory (program_checked,               /*   so check the bounds */
                             PL - field & LA_MASK, &label); /*     and then read the label */

        cpu_flush ();                                   /* flush the TOS registers to memory */

        if (SM > Z) {                                   /* if the stack limit was exceeded */
            if (field == 0) {                           /*   then if the label was on the TOS */
                cpu_push ();                            /*     then push the stack down */
                RA = label;                             /*       and restore the label to the TOS */
                }

            MICRO_ABORT (trap_Stack_Overflow);          /* trap for a stack overflow */
            }

        if (label & LABEL_EXTERNAL)                     /* if the label is non-local */
            MICRO_ABORTP (trap_STT_Violation, STA);     /*   then trap for an STT violation */

        cpu_push ();                                    /* push the stack down */
        RA = P - 1 - PB & R_MASK;                       /*   and store the return address on the TOS */

        new_p = PB + (label & LABEL_ADDRESS_MASK);      /* get the subroutine entry address */

        cpu_read_memory (fetch_checked, new_p, &NIR);   /* check the bounds and get the first instruction */
        P = new_p + 1 & R_MASK;                         /*   and set P to point at the next instruction */
        break;


    case 002:                                           /* PCAL (none; STUN, STOV, CSTV, STTV, ABS CST, TRACE, UNCAL, BNDV) */
        if (field == 0) {                               /* if the label is on the TOS */
            PREADJUST_SR (1);                           /*   then ensure a valid TOS register */
            label = RA;                                 /*     before getting the label */
            cpu_pop ();                                 /*       and popping it from the stack */
            }

        else                                                /* otherwise, the label is at M [PL-N] */
            cpu_read_memory (program_checked,               /*   so check the bounds */
                             PL - field & LA_MASK, &label); /*     and then read the label */

        cpu_flush ();                                   /* flush the TOS registers to memory */

        if (SM > Z) {                                   /* if the stack limit was exceeded */
            if (field == 0) {                           /*   then if the label was on the TOS */
                cpu_push ();                            /*     then push the stack down */
                RA = label;                             /*       and restore the label to the TOS */
                }

            MICRO_ABORT (trap_Stack_Overflow);          /* trap for a stack overflow */
            }

        cpu_mark_stack ();                              /* write a stack marker */

        cpu_call_procedure (label, 0);                  /* set up PB, P, PL, and STA to call the procedure */
        break;


    case 003:                                           /* EXIT (CC; STUN, STOV, MODE, CSTV, TRACE, ABSCST, BNDV) */
        if (SM < Q)                                     /* if the stack memory pointer is below the stack marker */
            cpu_flush ();                               /*   then flush the TOS registers to memory */

        SR = 0;                                         /* invalidate the TOS registers */

        new_sm = Q - 4 - field & R_MASK;                /* compute the new stack pointer value */

        cpu_read_memory (stack, Q, &operand);           /* read the delta Q value from the stack marker */
        new_q = Q - operand & R_MASK;                   /*  and determine the new Q value */

        cpu_exit_procedure (new_q, new_sm, field);      /* set up the return code segment and stack */
        break;


    case 004:                                           /* SXIT (none; STUN, STOV, BNDV) */
        new_p = RA + PB & R_MASK;                       /* get the return address */
        cpu_read_memory (fetch_checked, new_p, &NIR);   /* check the bounds and then load the NIR */

        cpu_pop ();                                     /* pop the return address from the stack */

        if (field > 0 && SR > 0)                        /* if an adjustment is wanted and the TOS registers are occupied */
            cpu_flush ();                               /*   then flush the registers to memory */

        new_sm = SM - field & R_MASK;                   /* adjust the stack pointer as requested */

        check_stack_bounds (new_sm);                    /* trap if the new value is outside of the stack */
        SM = new_sm;                                    /*   before setting the new stack pointer value */

        P = new_p + 1 & R_MASK;                         /* set the new P value for the return */
        break;


    case 005:                                           /* ADXI (CCA; none) */
        X = X + field & R_MASK;                         /* add the immediate value to X */
        SET_CCA (X, 0);                                 /*    and set the condition code */
        break;


    case 006:                                           /* SBXI (CCA; none) */
        X = X - field & R_MASK;                         /* subtract the immediate value from X */
        SET_CCA (X, 0);                                 /*   and set the condition code */
        break;


    case 007:                                               /* LLBL (none; STOV, STTV) */
        cpu_read_memory (program_checked, PL, &stt_length); /* read the STT length */

        if ((stt_length & STT_LENGTH_MASK) < field)     /* if the STT index is not within the STT */
            MICRO_ABORTP (trap_STT_Violation, STA);     /*   then trap for an STT violation */

        cpu_read_memory (program_checked,               /* check the bounds */
                         PL - field & LA_MASK, &label); /*   and then read the label */

        if ((label & LABEL_EXTERNAL) == 0)              /* if the label is a local label */
            if (field > LABEL_STTN_MAX)                 /*   then if the STT number is too big for an external */
                MICRO_ABORTP (trap_STT_Violation, STA); /*     then trap for an STT violation */

            else                                        /*   otherwise */
                label = LABEL_EXTERNAL                  /*     convert it to an external label */
                          | (field << LABEL_STTN_SHIFT) /*       by merging the STT number */
                          | STATUS_CS (STA);            /*         with the currently executing segment number */

        cpu_push ();                                    /* push the stack down */
        RA = label;                                     /*   and store the label on the TOS */
        break;


    case 010:                                           /* LDPP (CCA; STOV, BNDV) */
    case 011:                                           /* LDPN (CCA; STOV, BNDV) */
        cpu_ea (CIR & MODE_DISP_MASK,                   /* get the address of the first word */
                &class, &offset, NULL);

        if (offset < PB && NPRV)                        /* if the offset is below PB and the mode is not privileged */
            MICRO_ABORT (trap_Bounds_Violation);        /*   then trap for a bounds violation */

        cpu_push ();                                    /* push the stack down */

        if (offset >= PL && NPRV)                       /* if the offset is at or above PL and the mode is not privileged */
            MICRO_ABORT (trap_Bounds_Violation);        /*   then trap for a bounds violation */

        cpu_push ();                                    /* push the stack down again */

        cpu_read_memory (program, offset, &operand);    /* read the first word */
        RB = operand;                                   /*   and store it in the NOS */

        offset = offset + 1 & LA_MASK;                  /* point at the second word */

        cpu_read_memory (program, offset, &operand);    /* read the second word */
        RA = operand;                                   /*   and store the on the TOS */

        SET_CCA (RB, RA);                               /* set the condition code */
        break;


    case 012:                                           /* ADDS (none; STUN, STOV) */
        if (field == 0)                                 /* if the immediate value is zero */
            field = RA - 1;                             /*   then use the TOS value - 1 instead */

        cpu_flush ();                                   /* empty the TOS registers */

        new_sm = SM + field & R_MASK;                   /* get the new stack pointer value */

        check_stack_bounds (new_sm);                    /* trap if the new value is outside of the stack */
        SM = new_sm;                                    /*   before setting the new stack pointer value */
        break;


    case 013:                                           /* SUBS (none; STUN, STOV) */
        if (field == 0)                                 /* if the immediate value is zero */
            field = RA + 1;                             /*   then use the TOS value + 1 instead */

        cpu_flush ();                                   /* empty the TOS registers */

        new_sm = SM - field & R_MASK;                   /* get the new stack pointer value */

        check_stack_bounds (new_sm);                    /* trap if the new value is outside of the stack */
        SM = new_sm;                                    /*   before setting the new stack pointer value */
        break;


    case 014:
        status = STOP_UNIMPL;                           /* opcodes 036000-036777 are unimplemented */
        break;


    case 015:                                           /* ORI (CCA; STUN) */
        RA = RA | field;                                /* logically OR the TOS and the immediate value */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 016:                                           /* XORI (CCA; STUN) */
        RA = RA ^ field;                                /* logically XOR the TOS and the immediate value */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;


    case 017:                                           /* ANDI (CCA; STUN) */
        RA = RA & field;                                /* logically AND the TOS and the immediate value */
        SET_CCA (RA, 0);                                /*   and set the condition code */
        break;

     }                                                  /* all cases are handled  */

return status;                                          /* return the execution status */
}



/* CPU base set local utility routines */


/* Add two 32-bit numbers.

   Two 32-bit values are added, and the 32-bit sum is returned.  The C (carry)
   bit in the status register is set if the result is truncated and cleared
   otherwise.  The O (overflow) bit is set if the result exceeds the maximum
   positive or negative range, i.e., the result overflows into the sign bit.  In
   addition, an integer overflow interrupt (ARITH trap) occurs if the user trap
   bit is set.
*/

static uint32 add_32 (uint32 augend, uint32 addend)
{
t_uint64 sum;

sum = (t_uint64) augend + (t_uint64) addend;            /* sum the values */

SET_CARRY (sum > D32_UMAX);                             /* set C if there is a carry out of the MSB */

SET_OVERFLOW (D32_SIGN                                  /* set O if the signs */
                & (~augend ^ addend)                    /*   of the operands are the same */
                & (augend ^ sum));                      /*     but the sign of the result differs */

return (uint32) sum & D32_MASK;                         /* return the lower 32 bits of the sum */
}


/* Subtract two 32-bit numbers.

   Two 32-bit values are subtracted, and the 32-bit difference is returned.  The
   C (carry) bit in the status register is set if the subtraction did not
   require a borrow for the most-significant bit.  The O (overflow) bit is set
   if the result exceeds the maximum positive or negative range, i.e., the
   result borrows from the sign bit.  In addition, an integer overflow interrupt
   (ARITH trap) occurs if the user trap bit is set.


   Implementation notes:

    1. The carry bit is set to the complement of the borrow, i.e., carry = 0 if
       there is a borrow and 1 is there is not.
*/

static uint32 sub_32 (uint32 minuend, uint32 subtrahend)
{
t_uint64 difference;

difference = (t_uint64) minuend - (t_uint64) subtrahend;    /* subtract the values */

SET_CARRY (subtrahend <= minuend);                          /* set C if no borrow from the MSB was done */

SET_OVERFLOW (D32_SIGN                                      /* set O if the signs */
                & (minuend ^ subtrahend)                    /*   of the operands differ */
                & (minuend ^ difference));                  /*     as do the signs of the minuend and result */

return (uint32) difference & D32_MASK;                      /* return the lower 32 bits of the difference */
}


/* Shift single- and double-word operands.

   An arithmetic, logical, or circular left or right shift is performed in place
   on the 16-bit or 32-bit operand in RA or RB and RA, respectively.  Condition
   code A is set for the result.  The shift count and shift direction are
   derived from the instruction supplied.

   An arithmetic left shift retains the sign bit; an arithmetic right shift
   copies the sign bit.  Logical shifts fill zeros into the LSB or MSB.
   Circular shifts rotate bits out of the MSB and into the LSB, or vice versa.

   On entry, the shift count is extracted from the instruction.  If the
   instruction is indexed, the value in the X register is added to the count.

   For the type of shift selected, the fill bits are determined: sign bits fill
   for an arithmetic shift, zero bits fill for a logical shift, and operand bits
   fill for a circular shift.  The result of a shift in excess of the operand
   size is also determined.

   If the shift count is zero, then the result is the original operand.
   Otherwise, if the count is less than the operand size, the selected shift is
   performed.  A right shift of any type is done by shifting the operand and
   filling with bits of the appropriate type.  An arithmetic left shift is done
   by shifting the operand and restoring the sign.  A logical or circular shift
   is done by shifting the operand and filling with bits of the appropriate
   type.

   The result is restored to the TOS register(s), and CCA is set before
   returning.


   Implementation notes:

    1. An arithmetic left shift must be handled as a special case because the
       shifted operand bits "skip over" the sign bit.  That is, the bits are
       lost from the next-most-significant bit while preserving the MSB.  For
       all other shifts, including the arithmetic right shift, the operand may
       be shifted and then merged with the appropriate fill bits.

    2. The C standard specifies that the results of bitwise shifts with counts
       greater than the operand sizes are undefined, so we must handle excessive
       shifts explicitly.

    3. The C standard specifies that the results of bitwise shifts with negative
       signed operands are undefined (for left shifts) or implementation-defined
       (for right shifts).  Therefore, we must use unsigned operands and handle
       arithmetic shifts explicitly.

    4. The compiler requires a "default" case (instead of a "normalizing"
       case) for the switch statement.  Otherwise, it will complain that
       "fill" and "result" are potentially undefined, even though all
       enumeration values are covered.
*/

static void shift_16_32 (HP_WORD opcode, SHIFT_TYPE shift, OPERAND_SIZE op_size)
{
typedef struct {
    uint32 sign;                                /* the sign bit of the operand */
    uint32 data;                                /* the data mask of the operand */
    uint32 width;                               /* the width of the operand in bits */
    } PROPERTY;

static const PROPERTY prop [2] = {
    { D16_SIGN, D16_MASK & ~D16_SIGN, D16_WIDTH },      /* 16-bit operand properties */
    { D32_SIGN, D32_MASK & ~D32_SIGN, D32_WIDTH }       /* 32-bit operand properties */
    };

uint32 count, operand, fill, result;

count = SHIFT_COUNT (opcode);                           /* get the shift count from the instruction */

if (opcode & X_FLAG)                                    /* if the instruction is indexed */
    count = count + X & SHIFT_COUNT_MASK;               /*   then add the index to the count modulo 64 */

operand = RA;                                           /* get the (lower half of the) operand */

if (op_size == size_32)                                 /* if the operand size is 32 bits */
    operand = RB << D16_WIDTH | operand;                /*   then merge the upper half of the operand */

switch (shift) {                                        /* dispatch the shift operation */

    case arithmetic:                                    /* for an arithmetic shift */
        fill = operand & prop [op_size].sign ? ~0 : 0;  /*   fill with copies of the sign bit */

        if (opcode & SHIFT_RIGHT_FLAG)                  /* for a right shift */
            result = fill;                              /*   the excessive shift result is all fill bits */
        else                                            /* whereas for a left shift */
            result = prop [op_size].sign;               /*   the excessive shift result is just the sign bit */
        break;

    case logical:                                       /* for a logical shift */
        fill = 0;                                       /*   fill with zeros */
        result = 0;                                     /* the excessive shift result is all zeros */
        break;

    case circular:                                      /* for a circular shift */
        fill = operand;                                 /*   fill with the operand */
        count = count % prop [op_size].width;           /* an excessive shift count is reduced modulo the word width */
        result = 0;                                     /*   so there is no excessive shift result */
        break;

    default:                                            /* normalizing shifts are not used */
        return;
    }


if (count == 0)                                             /* if the shift count is zero */
    result = operand;                                       /*   then the result is the operand value */

else if (count < prop [op_size].width)                      /* otherwise if the shift count is not excessive */
    if (opcode & SHIFT_RIGHT_FLAG)                          /*   then if this is a right shift of any type */
        result = operand >> count                           /*     then shift the operand */
                   | fill << prop [op_size].width - count;  /*       and fill with fill bits */

    else if (shift == arithmetic)                           /* otherwise if this is an arithmetic left shift */
        result = operand << count & prop [op_size].data     /*   then shift the operand */
                   | fill & prop [op_size].sign;            /*     and restore the sign bit */

    else                                                    /* otherwise this is a logical or circular left shift */
        result = operand << count                           /*   so shift the operand */
                   | fill >> prop [op_size].width - count;  /*     and fill with fill bits */


RA = LOWER_WORD (result);                               /* store the lower word on the TOS */

if (op_size == size_16)                                 /* if the operand is a single word */
    SET_CCA (RA, 0);                                    /*   then set the condition code */

else {                                                  /* otherwise the operand is a double word */
    RB = UPPER_WORD (result);                           /*   so store the upper word in the NOS */
    SET_CCA (RB, RA);                                   /*     and then set the condition code */
    }

return;
}


/* Shift triple- and quad-word operands.

   An arithmetic left or right shift or normalizing left shift is performed
   in place on the 48-bit or 64-bit operand in RC/RB/RA or RD/RC/RB/RA,
   respectively.  Condition code A is set for the result.  The shift count and
   shift direction are derived from the instruction supplied.

   An arithmetic left shift retains the sign bit; an arithmetic right shift
   copies the sign bit.  A normalizing shift does not specify a shift count.
   Instead, the operand is shifted until bit 6 is set, bits 0-5 are cleared, and
   the shift count is returned in the X register.

   On entry for an arithmetic shift, the shift count is extracted from the
   instruction.  If the instruction is indexed, the value in the X register is
   added to the count.  If the shift count is zero, then the result is the
   original operand.  Otherwise, if the count is less than the operand size, the
   selected shift is performed.  A right shift is done by shifting the operand
   and filling with sign bits.  A left shift is done by shifting the operand and
   restoring the sign.

   For a normalizing shift with at least one bit set to the right of bit 5, the
   operand is left-shifted and X is incremented until bit 6 is set.  Bits 0-5
   are then masked off.  If no bits are set to the right of bit 5, X is set to,
   or incremented by, the maximum shift count, CCE is set, and the operand is
   not altered.

   After a successful shift, the result is restored to the TOS registers, and
   CCA is set before returning.


   Implementation notes:

    1. Logical and circular shifts are unsupported as they are not offered by
       the instruction set.

    2. All of the shift instructions except QASL and QASR use bit 9 to indicate
       a left (0) or right (1) shift and bit 4 to indicate that the shift count
       includes the index register value.  Bit 9 is always on for QASL and QASR,
       which use bit 4 to indicate a left or right shift, and which always
       include the index register value.  To simplify handling, the QASL and
       QASR executors move the left/right indication to bit 9 and set bit 4 on
       before calling this routine.
*/

static void shift_48_64 (HP_WORD opcode, SHIFT_TYPE shift, OPERAND_SIZE op_size)
{
typedef struct {
    t_uint64 sign;                              /* the sign bit of the operand */
    t_uint64 data;                              /* the data mask of the operand */
    uint32   width;                             /* the width of the operand in bits */
    uint32   padding;                           /* unused padding to suppress an alignment warning */
    } PROPERTY;

static const PROPERTY prop [4] = {
    { 0, 0, 0 },                                        /* (unused 16-bit properties) */
    { 0, 0, 0 },                                        /* (unused 32-bit properties) */
    { D48_SIGN, D48_MASK & ~D48_SIGN, D48_WIDTH },      /* 48-bit operand properties */
    { D64_SIGN, D64_MASK & ~D64_SIGN, D64_WIDTH }       /* 64-bit operand properties */
    };

uint32   count;
t_uint64 operand, fill, result;

if (op_size == size_48)                                 /* if the operand size is 48 bits */
    operand = TO_QWORD (0, RC, RB, RA);                 /*   then merge the three words of the operand */
else                                                    /* otherwise the operand size is 64 bits */
    operand = TO_QWORD (RD, RC, RB, RA);                /*   so merge all four operand words */

if (shift == arithmetic) {                              /* if this is an arithmetic shift */
    count = SHIFT_COUNT (opcode);                       /*   then the instruction contains the shift count */

    if (opcode & X_FLAG)                                /* if the instruction is indexed */
        count = count + X & SHIFT_COUNT_MASK;           /*   then add the index to the count modulo 64 */

    fill = operand & prop [op_size].sign ? ~0 : 0;      /* filling will use copies of the sign bit */

    if (count == 0)                                     /* if the shift count is zero */
        result = operand;                               /*   then the result is the operand value */

    else if (count < prop [op_size].width)                      /* otherwise if the shift count is not excessive */
        if (opcode & SHIFT_RIGHT_FLAG)                          /*   then if this is a right shift */
            result = operand >> count                           /*     then shift the operand */
                       | fill << prop [op_size].width - count;  /*       and fill with fill bits */
        else                                                    /* otherwise it is a left shift */
            result = operand << count & prop [op_size].data     /*   so shift the operand */
                       | fill & prop [op_size].sign;            /*     and restore the sign bit */

    else                                                /* otherwise the shift count exceeds the operand size */
        if (opcode & SHIFT_RIGHT_FLAG)                  /*   so if this is a right shift */
            result = fill;                              /*     then the excessive shift result is all fill bits */
        else                                            /*   whereas for a left shift */
            result = prop [op_size].sign;               /*     the excessive shift result is just the sign bit */
    }

else if (shift == normalizing) {                        /* otherwise if this is a (left) normalizing shift */
    if ((opcode & X_FLAG) == 0)                         /*   then if the instruction is not indexed */
        X = 0;                                          /*     then clear the shift count */

    if (operand & NORM_MASK) {                          /* if there's at least one unnormalized bit set */
        result = operand;                               /*   then start with the operand */

        while ((result & NORM_BIT) == 0) {              /* while the result is unnormalized */
            result = result << 1;                       /*   left-shift the result */
            X = X + 1;                                  /*     and increment the shift count */
            }

        result = result & NORM_MASK;                    /* mask off the leading bits */
        X = X & R_MASK;                                 /*   and wrap the count value */
        }

    else {                                              /* otherwise there are no bits to normalize */
        X = X + 42 & R_MASK;                            /*   so report the maximum shift count */

        SET_CCE;                                        /* set the condition code */
        return;                                         /*   and return with the operand unmodified */
        }
    }

else                                                    /* otherwise the shift type */
    return;                                             /*   is not supported by this routine */

RA = LOWER_WORD (result);                               /* restore the */
RB = UPPER_WORD (result);                               /*   lower three words */
RC = LOW_UPPER_WORD (result);                           /*     to the stack */

if (op_size == size_48)                                 /* if the operand size is 48 bits */
    SET_CCA (RC, RB | RA);                              /*   then set the condition code */

else {                                                  /* otherwise the size is 64 bits */
    RD = HIGH_UPPER_WORD (result);                      /*   so restore the upper word */
    SET_CCA (RD, RC | RB | RA);                         /*     and then set the condition code */
    }

return;
}


/* Check a value against the stack bounds.

   This routine checks a new frame (Q) or stack memory (SM) pointer value to
   ensure that it is within the stack bounds.  If the value does not lie between
   DB and Z, a trap will occur.

   The SETR instruction sets the frame and stack pointers, and the SXIT, ADDS,
   and SUBS instructions adjust the stack pointer.  Each verifies that the new
   value is between DB and Z before storing the value in the Q or SM register.
   If the value is greater than Z, a stack overflow trap is taken; if the value
   is less than DB, a stack underflow trap is taken.


   Implementation notes:

    1. Conceptually, ADDS can only exceed Z, whereas SXIT and SUBS can only drop
       below DB.  However, the microcode for all three instructions checks that
       both Z - new_SM and new_SM - DB are positive; if not, the routine traps
       to stack overflow or underflow, respectively.  As the new SM value is
       calculated modulo 2^16, wraparound overflows and underflows are caught
       only if they are within 32K of the Z or DB values.  For full coverage,
       both tests are necessary for each call, as an ADDS wraparound of 48K,
       e.g., would be caught as a stack underflow.  Simulation performs the same
       tests to obtain the same behavior, rather than checking that new_SM <= Z
       and DB <= new_SM.

    2. 32-bit subtractions are performed to ensure that wraparound overflows are
       caught.
*/

static void check_stack_bounds (HP_WORD new_value)
{
if ((uint32) Z - new_value > D16_SMAX)                  /* if the new value is not within 32K below Z */
    MICRO_ABORT (trap_Stack_Overflow);                  /*   then trap for an overflow */

else if ((uint32) new_value - DB > D16_SMAX && NPRV)    /* otherwise if the new value is not within 32K above DB */
    MICRO_ABORT (trap_Stack_Underflow);                 /*   then trap for an underflow unless the mode is privileged */

else                                                    /* otherwise the new value */
    return;                                             /*   is within the stack bounds */
}


/* Perform a test, control, or set interrupt I/O operation.

   The I/O operation specified in the "command" parameter is sent to the device
   whose device number stored on the stack at location S - K.  The K-field of
   the I/O instruction present in the CIR is extracted and subtracted from the
   current stack pointer.  The resulting memory location is read, and the lower
   byte is used as the device number.  The I/O command is sent, along with the
   value in the TOS for a CIO instruction, and the result is obtained.

   If the device number is invalid, an I/O timeout will result.  If this occurs,
   the timeout flag in CPX1 is reset, condition code "less than" is set, and
   this routine returns 0.  Otherwise, condition code "equal" is set to indicate
   success, and the device and result values are merged and returned (which will
   be non-zero, because zero is not a valid device number).


   Implementation notes:

    1. A checked access to memory is requested to obtain the device number.  As
       privileged mode has been previously ascertained, the memory check serves
       only to return a TOS register value if the resulting address is between
       SM and SR.
*/

static uint32 tcs_io (IO_COMMAND command)
{
uint32  address;
HP_WORD device, result;

if (NPRV)                                               /* if the mode is not privileged */
    MICRO_ABORT (trap_Privilege_Violation);             /*   then abort with a privilege violation */

address = SM + SR - IO_K (CIR) & LA_MASK;               /* get the location of the device number */

cpu_read_memory (stack, address, &device);              /* read it from the stack or TOS registers */
device = LOWER_BYTE (device);                           /*   and use only the lower byte of the value */

result = iop_direct_io (device, command,                /* send the I/O order to the device */
                        (command == ioCIO ? RA : 0));   /*   along with the control word for a CIO instruction */

if (CPX1 & cpx1_IOTIMER) {                              /* if an I/O timeout occurred */
    CPX1 &= ~cpx1_IOTIMER;                              /*   then clear the timer */
    SET_CCL;                                            /*     and set condition code "less than" */
    return 0;                                           /*        and fail the instruction */
    }

else {                                                  /* otherwise */
    SET_CCE;                                            /*   set the condition code for success */
    return TO_DWORD (device, result);                   /*     and return the (non-zero) device and result values */
    }
}


/* Perform a start, read, or write I/O operation.

   The I/O operation specified in the "command" parameter is sent to the device
   whose device number stored on the stack at location S - K, where K is the
   K-field value of the I/O instruction present in the CIR.  A Test I/O order is
   first sent to the device to determine if it is ready.  If the device number
   is invalid, the routine returns zero with condition code "less than" set to
   indicate failure.  If the Test I/O succeeded, the device number and test
   result are obtained.

   The test result is checked to see if the bit specified by the "ready_flag"
   parameter is set.  If it is not, then the device is not ready, so the test
   result is pushed onto the TOS, condition code "greater than" is set, and zero
   is returned to indicate failure.  If the bit is set, the device is ready for
   the operation.

   For a Start I/O order, the starting address of the I/O program, located on
   the TOS, is stored in the first word of the Device Reference Table entry
   corresponding to the device number.  The I/O command is sent, along with the
   value in the TOS for a WIO instruction, and the result is obtained.
   Condition code "equal" is set to indicate success, and the device and result
   values are merged and returned (which will be non-zero, because zero is not a
   valid device number).


   Implementation notes:

    1. The initial Test I/O order verifies that the mode is privileged and that
       the device number is valid.  Therefore, the result of the command
       operation need not be tested for validity.
*/

static uint32 srw_io (IO_COMMAND command, HP_WORD ready_flag)
{
uint32  test;
HP_WORD device, result;

test = tcs_io (ioTIO);                                  /* send a Test I/O order to the device */

if (test == 0)                                          /* if an I/O timeout occurred */
    return 0;                                           /*   then return 0 with CCL set to fail the instruction */

device = UPPER_WORD (test);                             /* split the returned value */
result = LOWER_WORD (test);                             /*   into the device number and test result */

if (result & ready_flag) {                              /* if the device is ready */
    if (command == ioSIO)                               /*   then if this is an SIO order */
        cpu_write_memory (absolute, device * 4, RA);    /*     then write the I/O program address to the DRT */

    result = iop_direct_io (device, command,                /* send the I/O order to the device */
                            (command == ioWIO ? RA : 0));   /*   along with the data word for a WIO instruction */

    SET_CCE;                                            /* set the condition code for success */
    return TO_DWORD (device, result);                   /*   and return the (non-zero) device and result values */
    }

else {                                                  /* otherwise the device is not ready */
    cpu_push ();                                        /*   so push the stack down */
    RA = result;                                        /*     and store the TIO response on the TOS */

    SET_CCG;                                            /* set the condition code to indicate "not ready" */
    return 0;                                           /*   and fail the instruction */
    }
}


/* Decrement the stack pointer.

   Pop values from the stack until the stack pointer has been decremented by the
   amount indicated by the "decrement" parameter.

   The word and byte move and comparison instructions include a stack decrement
   field that may be zero or a positive value indicating the number of words to
   remove at the end of the instruction.  This routine is called to implement
   this feature.

   Note that the stack decrement is performed only at the completion of these
   instructions.  If an instruction is interrupted, the decrement is not done,
   as the parameters on the stack will be needed when execution of the
   instruction is resumed after the interrupt handler completes.
*/

static void decrement_stack (uint32 decrement)
{
while (decrement > 0) {                                 /* decrement the stack pointer */
    cpu_pop ();                                         /*   by the count specified */
    decrement = decrement - 1;                          /*     by the instruction */
    }

return;
}


/* Move a block of words in memory.

   A block of words is moved from a source address to a destination address.  If
   a pending interrupt is detected during the move, the move is interrupted to
   service it.  Otherwise at the completion of the move, the stack is
   decremented by the amount indicated.

   On entry, the "source_class" parameter indicates the memory classification
   for source reads.  If the classification is absolute, the "source_base"
   parameter contains the physical address (i.e., memory bank and offset) of the
   base of the first word to move.  If it is not absolute, the parameter
   contains the offset within the bank implied by the classification.
   Similarly, the "dest_class" and "dest_base" parameters designate the base of
   the first word to write.  The "decrement" parameter contains the number of
   stack words to delete if the move completed successfully.

   If the source is absolute, the TOS registers RA, RB, and RD contain the
   count, source offset from the source base, and destination offset from the
   destination base, respectively.  Otherwise, the the TOS registers RA, RB, and
   RC contain the count and bases.

   Register RA contains an unsigned (positive) word count when called for the
   MTDS and MFDS instructions, and a signed word count otherwise.  If the word
   count is negative, the move is performed in reverse order, i.e., starting
   with the last word of the block and ending with the first word of the block.
   If the word count is zero on entry, the move is skipped, but the stack
   decrement is still performed.

   On exit, the TOS registers are updated for the block (or partial block, in
   the case of an intervening interrupt), and normal or error status from the
   interrupt check is returned.


   Implementation notes:

    1. This routine implements the MVWS microcode subroutine.

    2. The type of count (unsigned or signed) is determined by whether or not
       the CIR holds an MTDS or MFDS instruction.

    3. Incrementing and masking of the TOS registers must be done after each
       word is moved, rather than at loop completion, so that an interrupt will
       flush the correct TOS values to memory.
*/

static t_stat move_words (ACCESS_CLASS source_class, uint32 source_base,
                          ACCESS_CLASS dest_class,   uint32 dest_base,
                          uint32 decrement)
{
HP_WORD operand, *RX;
uint32  increment, source_bank, dest_bank;
t_stat  status;

if (RA & D16_SIGN && (CIR & MTFDS_MASK) != MTFDS)       /* if the count is signed and negative */
    increment = 0177777;                                /*   then the memory increment is negative */
else                                                    /* otherwise */
    increment = 1;                                      /*   the increment is positive */

source_bank = source_base & ~LA_MASK;                   /* extract the source bank */
dest_bank   = dest_base   & ~LA_MASK;                   /*   and destination bank in case they are needed */

if (source_class == absolute)                           /* if the source transfer is absolute */
    RX = & RD;                                          /*   then the destination offset is in RD */
else                                                    /* otherwise */
    RX = & RC;                                          /*   it is in RC */

while (RA != 0) {                                       /* while there are words to move */
    cpu_read_memory (source_class,                      /*   read a source word */
                     source_bank | source_base + RB & LA_MASK,
                     &operand);

    cpu_write_memory (dest_class,                       /* move it to the destination */
                      dest_bank | dest_base + *RX & LA_MASK,
                      operand);

    RA  = RA  - increment & R_MASK;                     /* update the count */
    RB  = RB  + increment & R_MASK;                     /*   and the source */
    *RX = *RX + increment & R_MASK;                     /*     and destination offsets */

    if (cpu_interrupt_pending (&status))                /* if an interrupt is pending */
        return status;                                  /*   then return with an interrupt set up or an error */
    }

decrement_stack (decrement);                            /* decrement the stack as indicated */
return SCPE_OK;                                         /*   and return the success of the move */
}



/* CPU base set local instruction execution routines */


/* Execute a move or special instruction (subopcode 02, field 00).

   This routine is called to execute the move or register instruction currently
   in the CIR.  The instruction formats are:

       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   0 |  move op  | opts/S decrement  |  Move
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   0 |  special op   | 0   0 | sp op |  Special
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   Byte move and compare instructions that specify byte counts (e.g., MVB, CMPB)
   bounds-check the starting and ending addresses to avoid checking each access
   separately.  Instructions that do not (e.g., SCW, MVBW) must bounds-check
   each access, as the counts are indeterminate.


   Implementation notes:

    1. CIR bits 8-12 are decoded to determine the instruction.  For some
       instructions, e.g., MOVE, bits 11 and 12 either designate options or are
       not decoded (i.e., are "don't care" bits).  These instructions are
       duplicated in the SR preadjustment table and carry multiple case labels
       in the instruction dispatcher.

    2. The IXIT, LOCK, PCN, and UNLK instructions decode bits 12-15, including
       the reserved bits 12 and 13.  The canonical forms have the reserved bits
       set to zero, but the hardware decodes bits 12-15 as IXIT = 0000, LOCK =
       nn01, PCN = nnn0, and UNLK = nn11 (where "n..." is any collective value
       other than 0).  If a non-canonical form is used, and the UNDEF stop is
       active, a simulation stop will occur.  If the stop is bypassed or not
       set, then the instruction will execute as in hardware.

       The LSEA, SSEA, LDEA, and SDEA instructions decode bits 14-15; the
       reserved bits 12-13 are not decoded and do not affect the instruction
       interpretation.

    3. Two methods of mapping non-canonical forms were examined: using a 16-way
       remapping table if SS_UNDEF was not set and a 4-way switch statement with
       the default returning STOP_UNDEF, or using a 16-way switch statement with
       the non-canonical values returning STOP_UNDEF if SS_UNDEF is set and
       falling into their respective executors otherwise.  The former required
       significantly more instructions and imposed a lookup penalty on canonical
       instructions for the non-stop case.  The latter used fewer instructions,
       imposed no penalty in the non-stop case, and the two extra tests required
       only three instructions each.  The latter method was adopted.

    4. The MVB and MVBW byte-move instructions perform read-modify-write actions
       for each byte moved.  This is inefficient -- each word is read and
       updated twice -- but it is necessary, as interrupts are checked after
       each byte is moved, and it is how the microcode handles these
       instructions.

    5. The MVBW instruction microcode performs bounds checks on the movement by
       determining the number of words from the source and target starting
       addresses to the address of the top of the stack (SM).  The smaller of
       these values is used as a count that is decremented within the move loop.
       When the count reaches zero, a bounds violation occurs if the mode is not
       privileged.  This is used instead of comparing the source and target
       addresses to SM to reduce the per-iteration checks from two to one.

    6. The IXIT microcode assumes that the machine is in privileged mode if the
       dispatcher-is-active flag is set.  In simulation, the privileged mode
       check is performed for all IXIT paths.

    7. When IXIT returns to a user process, the microcode sets the "trace flag"
       located at Q-13 in the ICS global area to -1.  The only description of
       this location is in the system tables manual, which says "flag set
       non-zero on IXIT away from ICS."  The action to set this value was added
       as a patch to the Series II microcode; however, this action is not
       present in the corresponding Series 64 microcode.  The description
       appears in the MPE V tables manual for version G.01.00 but is gone from
       the manual for version G.08.00.  No further information regarding the
       purpose of this flag has been found.

    8. The PCN microcode clears a TOS register via a queue-down operation, if
       necessary, before checking that the machine is in privileged mode.  In
       simulation, the check is performed before the register clear.  However,
       if a Mode Violation trap occurs, all of the TOS registers are flushed to
       memory, so the result is the same.
*/

static t_stat move_spec (void)
{
static const uint8 preadjustment [32] = {       /* stack preadjustment, indexed by operation */
    3, 3, 3, 3, 3, 3, 3, 3,                     /*   MOVE MOVE MOVE MOVE MVB  MVB  MVB  MVB  */
    4, 4, 2, 4, 4, 4, 2, 4,                     /*   MVBL MABS SCW  MTDS MVLB MDS  SCU  MFDS */
    2, 2, 2, 2, 3, 3, 3, 3,                     /*   MVBW MVBW MVBW MVBW CMPB CMPB CMPB CMPB */
    4, 4, 0, 0, 2, 2, 0, 0                      /*   RSW/LLSH  PLDA/PSTA xSEA/xDEA IXIT/etc. */
    };

int32        byte_count;
uint32       operation, address;
HP_WORD      operand, bank, offset, base;
HP_WORD      byte, test_byte, terminal_byte, increment, byte_class, loop_condition;
HP_WORD      source_bank, source, source_end, target_bank, target, target_end;
HP_WORD      stack_db, ics_q, delta_qi, disp_counter, delta_q, new_q, new_sm, device;
t_bool       q_is_qi, disp_active;
ACCESS_CLASS class;
t_stat       status = SCPE_OK;

operation = MSSUBOP (CIR);                              /* get the suboperation from the instruction */

PREADJUST_SR (preadjustment [operation]);               /* preadjust the TOS registers to the required number */

switch (operation) {                                    /* dispatch the move or special operation */

    case 000:                                           /* MOVE (none; STUN, STOV, BNDV) */
    case 001:
    case 002:
    case 003:
        if (RA != 0) {                                  /* if the word count is non-zero */
            if (RA & D16_SIGN)                          /*   then if it is negative */
                increment = 0177777;                    /*     then the memory increment is negative */
            else                                        /*   otherwise */
                increment = 1;                          /*     the increment is positive */

            while (SR > 3)                              /* if more than three TOS register are valid */
                cpu_queue_down ();                      /*   then queue them down until exactly three are left */

            if (CIR & DB_FLAG) {                                /* if the move is from the data segment */
                class = data;                                   /*   then set for data access */
                base = DB;                                      /*     and base the offset on DB */

                source = DB + RB & LA_MASK;                     /* determine the starting */
                source_end = source + RA - increment & LA_MASK; /*   and ending data source addresses */

                if (NPRV                                        /* if the mode is non-privileged */
                  && (source < DL || source > SM                /*   and the starting or ending address */
                  || source_end < DL || source_end > SM))       /*     is outside of the data segment */
                    MICRO_ABORT (trap_Bounds_Violation);        /*       then trap with a Bounds Violation */
                }

            else {                                              /* otherwise the move is from the code segment */
                class = program;                                /*   so set for program access */
                base = PB;                                      /*     and base the offset on PB */

                source = PB + RB & LA_MASK;                     /* determine the starting */
                source_end = source + RA - increment & LA_MASK; /*   and ending program source addresses */

                if (source < PB || source > PL                  /* if the starting or ending address */
                  || source_end < PB || source_end > PL)        /*   is outside of the code segment */
                    MICRO_ABORT (trap_Bounds_Violation);        /*     then trap with a Bounds Violation */
                }

            target = DB + RC & LA_MASK;                         /* calculate the starting */
            target_end = target + RA - increment & LA_MASK;     /*   and ending data target addresses */

            if (NPRV                                            /* if the mode is non-privileged */
              && (target < DL || target > SM                    /*   and the starting or ending target address */
              || target_end < DL || target_end > SM))           /*     is outside of the data segment */
                MICRO_ABORT (trap_Bounds_Violation);            /*       then trap with a Bounds Violation */

            status = move_words (class, base, data, DB,         /* move the words and adjust the stack */
                                 SDEC2 (CIR));
            }

        else                                            /* otherwise there are no words to move */
            decrement_stack (SDEC2 (CIR));              /*   so just adjust the stack as indicated */
        break;


    case 004:                                           /* MVB (none; STUN, STOV, BNDV) */
    case 005:
    case 006:
    case 007:
        while (SR > 3)                                  /* if more than three TOS register are valid */
            cpu_queue_down ();                          /*   then queue them down until exactly three are left */

        if (RA != 0) {                                  /* if the byte count is non-zero */
            if (RA & D16_SIGN)                          /*   then if it is negative */
                increment = 0177777;                    /*     then the memory increment is negative */
            else                                        /*   otherwise */
                increment = 1;                          /*     the increment is positive */

            if (CIR & DB_FLAG) {                                /* if the move is from the data segment */
                class = data;                                   /*   then classify as a data access */
                source = cpu_byte_ea (data_checked, RB, RA);    /*     and convert and check the byte address */
                }

            else {                                              /* otherwise the move is from the code segment */
                class = program;                                /*   so classify as a program access */
                source = cpu_byte_ea (program_checked, RB, RA); /*     and convert and check the byte address */
                }

            target = cpu_byte_ea (data_checked, RC, RA);    /* convert the target byte address and check the bounds */

            while (RA != 0) {                               /* while there are bytes to move */
                cpu_read_memory (class, source, &operand);  /*   read a source word */

                if (RB & 1)                                 /* if the byte address is odd */
                    byte = LOWER_BYTE (operand);            /*   then get the lower byte */
                else                                        /* otherwise the address is even */
                    byte = UPPER_BYTE (operand);            /*   so get the upper byte */

                if ((RB & 1) == (HP_WORD) (increment == 1)) /* if the last byte of the source word was accessed */
                    source = source + increment & LA_MASK;  /*   then update the word address */

                cpu_read_memory (data, target, &operand);   /* read the target word */

                if (RC & 1)                                     /* if the byte address is odd */
                    operand = REPLACE_LOWER (operand, byte);    /*   then replace the lower byte */
                else                                            /* otherwise the address is even */
                    operand = REPLACE_UPPER (operand, byte);    /*   so replace the upper byte */

                cpu_write_memory (data, target, operand);   /* write the word back */

                if ((RC & 1) == (HP_WORD) (increment == 1)) /* if the last byte of the target word was accessed */
                    target = target + increment & LA_MASK;  /*   then update the word address */

                RA = RA - increment & R_MASK;               /* update the count */
                RB = RB + increment & R_MASK;               /*   and the source */
                RC = RC + increment & R_MASK;               /*     and destination offsets */

                if (cpu_interrupt_pending (&status))        /* if an interrupt is pending */
                    return status;                          /*   then return with an interrupt set up or an error */
                }
            }

        decrement_stack (SDEC2 (CIR));                  /* adjust the stack as indicated by the instruction */
        break;


    case 010:                                           /* MVBL (none; STUN, STOV, MODE) */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        if (RA != 0) {                                  /* if the word count is non-zero */
            cpu_queue_down ();                          /*   then queue down so exactly three TOS registers are left */
            status = move_words (data, DB, stack, DL,   /*     and move the words and adjust the stack */
                                 SDEC2 (CIR));
            }

        else                                            /* otherwise there are no words to move */
            decrement_stack (SDEC2 (CIR));              /*   so just adjust the stack as indicated */
        break;


    case 011:                                           /* MABS (none; MODE, STUN) */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        if (RA != 0) {                                      /* if the word count is non-zero */
            cpu_read_memory (stack, SM, &target_bank);      /*   then get the target data bank number */

            status = move_words (absolute, TO_PA (RC, 0),   /* move the words and adjust the stack */
                                 absolute, TO_PA (target_bank, 0),
                                 SDEC3 (CIR));
            }

        else                                            /* otherwise there are no words to move */
            decrement_stack (SDEC3 (CIR));              /*   so just adjust the stack as indicated */
        break;


    case 012:                                           /* SCW (CCB, C; STUN, STOV, BNDV) */
    case 016:                                           /* SCU (C; STUN, STOV, BNDV) */
        while (SR > 2)                                  /* if more than two TOS register are valid */
            cpu_queue_down ();                          /*   then queue them down until exactly two are left */

        test_byte     = LOWER_BYTE (RA);                /* get the test byte */
        terminal_byte = UPPER_BYTE (RA);                /*   and the terminal byte */

        source = cpu_byte_ea (data_checked, RB, 0);     /* convert the source byte address and check the bounds */

        cpu_read_memory (data, source, &operand);       /* read the first word */

        while (TRUE) {
            if (RB & 1) {                               /* if the byte address is odd */
                if (cpu_interrupt_pending (&status))    /*   then if an interrupt is pending */
                    return status;                      /*     then return with an interrupt set up or an error */

                byte = LOWER_BYTE (operand);            /* get the lower byte */
                source = source + 1 & LA_MASK;          /*   and update the word address */

                if (NPRV && source > SM)                    /* if non-privileged and the address is out of range */
                    MICRO_ABORT (trap_Bounds_Violation);    /*   then trap for a bounds violation */

                cpu_read_memory (data, source, &operand);   /* read the next word */
                }

            else                                        /* otherwise the address is even */
                byte = UPPER_BYTE (operand);            /*   so get the upper byte */

            if (operation == 012)                       /* if this is the "scan while" instruction */
                if (byte == test_byte)                  /*   then if the byte matches the test byte */
                    RB = RB + 1 & R_MASK;               /*     then update the byte offset and continue */

                else {                                  /*   otherwise the "while" condition fails */
                    SET_CARRY (byte == terminal_byte);  /*     so set carry if the byte matches the terminal byte */
                    SET_CCB (byte);                     /*       and set the condition code */
                    break;                              /*         and terminate the scan */
                    }
            else                                        /* otherwise this is the "scan until" instruction */
                if (byte == terminal_byte) {            /*   so if the byte matches the terminal byte */
                    STA |= STATUS_C;                    /*     then set the carry flag */
                    break;                              /*       and terminate the scan */
                    }

                else if (byte == test_byte) {           /*   otherwise if the byte matches the test byte */
                    STA &= ~STATUS_C;                   /*     then clear the carry flag */
                    break;                              /*       and terminate the scan */
                    }

                else                                    /*   otherwise neither byte matches */
                    RB = RB + 1 & R_MASK;               /*    so update the byte offset and continue */
            }

        decrement_stack (SDEC2 (CIR));                  /* adjust the stack as indicated by the instruction */
        break;


    case 013:                                           /* MTDS (none; MODE, DSTB, STUN, ABSDST) */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        if (RA != 0) {                                      /* if the word count is non-zero */
            cpu_setup_data_segment (RD, &bank, &offset);    /*   then get the target segment bank and address */

            status = move_words (data, DB,                  /* move the words and adjust the stack */
                                 absolute, TO_PA (bank, offset),
                                 SDEC3 (CIR));
            }

        else                                            /* otherwise there are no words to move */
            decrement_stack (SDEC3 (CIR));              /*   so just adjust the stack as indicated */
        break;


    case 014:                                           /* MVLB (none; STUN, STOV, MODE) */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        if (RA != 0) {                                  /* if the word count is non-zero */
            cpu_queue_down ();                          /*   then queue down so exactly three TOS registers are left */

            status = move_words (stack, DL, data, DB,   /* move the words and adjust the stack */
                                 SDEC2 (CIR));
            }

        else                                            /* otherwise there are no words to move */
            decrement_stack (SDEC2 (CIR));              /*   so just adjust the stack as indicated */
        break;


    case 015:                                           /* MDS (none; MODE, DSTV, STUN, ABSDST) */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        if (RA != 0) {                                      /* if the word count is non-zero */
            cpu_read_memory (stack, SM, &operand);          /*   then get the target data segment number */

            cpu_setup_data_segment (operand, &target_bank,  /* get the target segment bank and address */
                                    &target);

            cpu_setup_data_segment (RC, &source_bank,       /*   and the source segment bank and address */
                                    &source);

            status = move_words (absolute, TO_PA (source_bank, source), /* move the words and adjust the stack */
                                 absolute, TO_PA (target_bank, target),
                                 SDEC3 (CIR));
            }

        else                                            /* otherwise there are no words to move */
            decrement_stack (SDEC3 (CIR));              /*   so just adjust the stack as indicated */
        break;


    case 017:                                           /* MFDS (none; MODE, DSTV, STUN, ABSDST) */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        if (RA != 0) {                                          /* if the word count is non-zero */
            cpu_setup_data_segment (RC, &bank, &offset);        /*   then get the source segment bank and address */

            status = move_words (absolute, TO_PA (bank, offset),    /* move the words and adjust the stack */
                                 data, DB, SDEC3 (CIR));
            }

        else                                            /* otherwise there are no words to move */
            decrement_stack (SDEC3 (CIR));              /*   so just adjust the stack as indicated */
        break;


    case 020:                                           /* MVBW (CCB; STUN, STOV, BNDV) */
    case 021:
    case 022:
    case 023:
        while (SR > 2)                                  /* if more than two TOS registers are valid */
            cpu_queue_down ();                          /*   then queue them down until exactly two are left */

        source = cpu_byte_ea (data_checked, RA, 0);     /* convert the source and target */
        target = cpu_byte_ea (data_checked, RB, 0);     /*   byte addresses and check the starting bounds */

        if (source > target) {                          /* if the source is closer to SM than the target */
            byte_count = (int32) (SM - source + 1) * 2; /*   then set the byte count from the source */

            if (RA & 1)                                 /* if starting with the lower byte */
                byte_count = byte_count - 1;            /*   then decrease the count by 1 */
            }

        else {                                          /* otherwise the target is closer to SM */
            byte_count = (int32) (SM - target + 1) * 2; /*   so set the byte count from the target */

            if (RB & 1)                                 /* if starting with the lower byte */
                byte_count = byte_count - 1;            /*   then decrease the count by 1 */
            }

        loop_condition = (CIR & MVBW_CCF) << MVBW_CCF_SHIFT;    /* get the loop condition code flags */

        while (TRUE) {                                  /* while the loop condition holds */
            cpu_read_memory (data, source, &operand);   /*   get the source word */

            if (RA & 1) {                               /* if the byte address is odd */
                byte = LOWER_BYTE (operand);            /*   then get the lower byte */
                source = source + 1 & LA_MASK;          /*     and update the word address */
                }

            else                                        /* otherwise the address is even */
                byte = UPPER_BYTE (operand);            /*   so get the upper byte */

            byte_class = cpu_ccb_table [byte];          /* classify the byte */

            if ((byte_class & loop_condition) == 0)     /* if the loop condition is false */
                break;                                  /*   then terminate the move */

            if (byte_class == CFE && CIR & MVBW_S_FLAG) /* if it's alphabetic and upshift is requested */
                byte = TO_UPPERCASE (byte);             /*   then upshift the character */

            if (byte_count == 0 && NPRV)                /* if source is beyond SM and not privileged */
                MICRO_ABORT (trap_Bounds_Violation);    /*   then trap for a bounds violation */

            cpu_read_memory (data, target, &operand);   /* read the target word */

            if (RB & 1)                                     /* if the byte address is odd */
                operand = REPLACE_LOWER (operand, byte);    /*   then replace the lower byte */
            else                                            /* otherwise the address is even */
                operand = REPLACE_UPPER (operand, byte);    /*   so replace the upper byte */

            cpu_write_memory (data, target, operand);   /* write the word back */

            if (RB & 1)                                 /* if the byte address is odd */
                target = target + 1 & LA_MASK;          /*   then update the word address */

            byte_count = byte_count - 1;                /* update the count */
            RA = RA + 1 & R_MASK;                       /*   and the source */
            RB = RB + 1 & R_MASK;                       /*     and destination offsets */

            if (cpu_interrupt_pending (&status))        /* if an interrupt is pending */
                return status;                          /*   then return with an interrupt set up or an error */
            }

        SET_CCB (byte);                                 /* set the condition code */

        decrement_stack (SDEC2 (CIR));                  /* adjust the stack as indicated by the instruction */
        break;


    case 024:                                           /* CMPB (CCx; STUN, STOV, BNDV) */
    case 025:
    case 026:
    case 027:
        while (SR > 3)                                  /* if more than three TOS register are valid */
            cpu_queue_down ();                          /*   then queue them down until exactly three are left */

        if (RA != 0) {                                  /* if the byte count is non-zero */
            if (RA & D16_SIGN)                          /*   then if it is negative */
                increment = 0177777;                    /*     then the memory increment is negative */
            else                                        /*   otherwise */
                increment = 1;                          /*     the increment is positive */

            if (CIR & DB_FLAG) {                                /* if the comparison is from the data segment */
                class = data;                                   /*   then classify as a data access */
                source = cpu_byte_ea (data_checked, RB, RA);    /*     and convert and check the byte address */
                }

            else {                                              /* otherwise the comparison is from the code segment */
                class = program;                                /*   so classify as a program access */
                source = cpu_byte_ea (program_checked, RB, RA); /*     and convert and check the byte address */
                }

            target = cpu_byte_ea (data_checked, RC, RA);    /* convert the target byte address and check the bounds */

            while (RA != 0) {                               /* while there are bytes to compare */
                cpu_read_memory (class, source, &operand);  /*   read a source word */

                if (RB & 1)                                 /* if the byte address is odd */
                    byte = LOWER_BYTE (operand);            /*   then get the lower byte */
                else                                        /* otherwise the address is even */
                    byte = UPPER_BYTE (operand);            /*   so get the upper byte */

                if ((RB & 1) == (HP_WORD) (increment == 1)) /* if the last byte of the source word was accessed */
                    source = source + increment & LA_MASK;  /*   then update the word address */

                cpu_read_memory (data, target, &operand);   /* read the target word */

                if (RC & 1)                                 /* if the byte address is odd */
                    test_byte = LOWER_BYTE (operand);       /*   then get the lower byte */
                else                                        /* otherwise the address is even */
                    test_byte = UPPER_BYTE (operand);       /*   so get the upper byte */

                if (test_byte != byte)                      /* if the bytes do not compare */
                    break;                                  /*   then terminate the loop */

                if ((RC & 1) == (HP_WORD) (increment == 1)) /* if the last byte of the target word was accessed */
                    target = target + increment & LA_MASK;  /*   then update the word address */

                RA = RA - increment & R_MASK;               /* update the count */
                RB = RB + increment & R_MASK;               /*   and the source */
                RC = RC + increment & R_MASK;               /*     and destination offsets */

                if (cpu_interrupt_pending (&status))        /* if an interrupt is pending */
                    return status;                          /*   then return with an interrupt set up or an error */
                }
            }

        if (RA == 0)                                    /* if the count expired */
            SET_CCE;                                    /*   then set condition code "equal" */

        else if (test_byte > byte)                      /* otherwise if the target byte > the source byte */
            SET_CCG;                                    /*   set condition code "greater than" */

        else                                            /* otherwise the target byte < the source byte */
            SET_CCL;                                    /*   so set condition code "less than" */

        decrement_stack (SDEC2 (CIR));                  /* adjust the stack as indicated by the instruction */
        break;


    case 030:                                           /* RSW and LLSH */
    case 031:
        if (CIR & 1) {                                  /* LLSH (CCx; STUN, MODE) */
            if (NPRV)                                   /* if the mode is not privileged */
                MICRO_ABORT (trap_Privilege_Violation); /*   then abort with a privilege violation */

            while (X > 0) {                             /* while the link count is non-zero */
                cpu_read_memory (absolute,              /*   read the target value */
                                 TO_PA (RB, RA + RD & LA_MASK),
                                 &target);

                if (target >= RC) {                     /* if the target is greater than or equal to the test word */
                    if (target == DV_UMAX)              /*   then if the target is the largest possible value */
                        SET_CCG;                        /*     then set condition code "greater than" */
                    else                                /*   otherwise */
                        SET_CCE;                        /*     set condition code "equal" */
                    break;                              /* end the search */
                    }

                address = TO_PA (RB, RA + 1 & LA_MASK); /* otherwise save the link offset address */

                cpu_read_memory (absolute, TO_PA (RB, RA), &RB);    /* read the next link bank */
                cpu_read_memory (absolute, address, &RA);           /*   and link offset */

                X = X - 1 & R_MASK;                     /* decrement the count */

                if (cpu_interrupt_pending (&status))    /* if an interrupt is pending */
                    return status;                      /*   then return with an interrupt set up or an error */
                }

            if (X == 0)                                 /* if the count expired */
                SET_CCL;                                /*   then set condition code "less than" */
            }

        else {                                          /* RSW (CCA; STUN, STOV) */
            cpu_push ();                                /* push the stack down */
            RA = SWCH;                                  /*   and set the TOS from the switch register */

            SET_CCA (RA, 0);                            /* set the condition code */
            }
        break;


    case 032:                                           /* PLDA and PSTA */
    case 033:
        if (PRIV)                                       /* if the mode is privileged */
            if (CIR & 1) {                              /* PSTA (none; STUN, MODE) */
                PREADJUST_SR (1);                       /* ensure a valid TOS register */
                cpu_write_memory (absolute_mapped,      /*   before writing the TOS to memory */
                                  X, RA);               /*     at address X */
                cpu_pop ();                             /*       and popping the stack */
                }

            else {                                      /* PLDA (CCA; STOV, MODE) */
                cpu_read_memory (absolute_mapped,       /* read the value at address X */
                                 X, &operand);
                cpu_push ();                            /* push the stack down */
                RA = operand;                           /*   and store the value on the TOS */

                SET_CCA (RA, 0);                        /* set the condition code */
                }

        else                                            /* otherwise the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   so abort with a privilege violation */
        break;


    case 034:                                           /* LSEA, SSEA, LDEA, and SDEA */
    case 035:
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        switch (SPECOP (CIR) & 3) {                     /* dispatch the special operation (bits 12-13 are not decoded) */

            case 000:                                   /* LSEA (CCA; STUN, STOV, MODE) */
                while (SR > 2)                          /* if more than two TOS register are valid */
                    cpu_queue_down ();                  /*   then queue them down until exactly two are left */

                address = TO_PA (RB, RA);               /* form the physical address */
                cpu_read_memory (absolute,              /*   and read the word from memory */
                                 address, &operand);

                cpu_push ();                            /* push the stack down */
                RA = operand;                           /*   and store the word on the TOS */

                SET_CCA (RA, 0);                        /* set the condition code */
                break;


            case 001:                                   /* SSEA (none; STUN, STOV, MODE) */
                PREADJUST_SR (3);                       /* ensure there are three valid TOS registers */

                while (SR > 3)                          /* if more than three TOS register are valid */
                    cpu_queue_down ();                  /*   then queue them down until exactly three are left */

                address = TO_PA (RC, RB);               /* form the physical address */
                cpu_write_memory (absolute,             /*   and write the word on the TOS to memory */
                                  address, RA);

                cpu_pop ();                             /* pop the TOS */
                break;


            case 002:                                   /* LDEA (CCA; STUN, STOV, MODE) */
                while (SR > 2)                          /* if more than two TOS register are valid */
                    cpu_queue_down ();                  /*   then queue them down until exactly two are left */

                address = TO_PA (RB, RA);               /* form the physical address */
                cpu_read_memory (absolute,              /*   and read the MSW from memory */
                                 address, &operand);

                cpu_push ();                            /* push the stack down */
                RA = operand;                           /*   and store the MSW on the TOS */

                address = TO_PA (RC, RB + 1 & LA_MASK); /* increment the physical address */
                cpu_read_memory (absolute,              /*   and read the LSW from memory */
                                 address, &operand);

                cpu_push ();                            /* push the stack down again */
                RA = operand;                           /*   and store the LSW on the TOS */

                SET_CCA (RB, RA);                       /* set the condition code */
                break;


            case 003:                                   /* SDEA (none; STUN, MODE) */
                PREADJUST_SR (4);                       /* ensure there are four valid TOS registers */

                address = TO_PA (RD, RC);               /* form the physical address */
                cpu_write_memory (absolute,             /* write the MSW from the NOS to memory */
                                  address, RB);

                address = TO_PA (RD, RC + 1 & LA_MASK); /* increment the physical address */
                cpu_write_memory (absolute,             /*   and write the LSW on the TOS to memory */
                                  address, RA);

                cpu_pop ();                             /* pop the TOS */
                cpu_pop ();                             /*   and the NOS */
                break;
            }                                           /* all cases are handled */
        break;


    case 036:                                           /* IXIT, LOCK, PCN, and UNLK */
    case 037:
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        switch (SPECOP (CIR)) {                         /* dispatch the special operation */

            case 000:                                   /* IXIT (none; MODE, STOV, CSTV, TRACE, ABS CST, BNDV) */
                SR = 0;                                 /* invalidate the TOS registers */

                cpu_read_memory (stack, Q, &delta_q);               /* read the stack marker link value */
                cpu_read_memory (absolute, ICS_Q, &ics_q);          /*   the stack marker initial value */
                cpu_read_memory (absolute, ics_q, &delta_qi);       /*     the dispatcher stack marker link */
                cpu_read_memory (absolute, ics_q - 18 & LA_MASK,    /*       and the dispatcher counter */
                                 &disp_counter);

                q_is_qi = (Q == ics_q);                     /* TRUE if Q = QI, i.e., a user process was interrupted */
                disp_active = (CPX1 & cpx1_DISPFLAG) != 0;  /* TRUE if the dispatcher is currently active */

                new_sm = 0;                                 /* these will be set by every path through IXIT */
                new_q  = 0;                                 /*   but the compiler doesn't realize this and so warns */

                if (!disp_active) {                         /* if not called by the dispatcher to start a process */
                    if (STATUS_CS (STA) > 1) {              /*   then if an external interrupt was serviced  */
                        cpu_read_memory (stack,             /*     then get the device number (parameter) */
                                         Q + 3 & LA_MASK,
                                         &device);

                        iop_direct_io (device, ioRIN, 0);   /* send a Reset Interrupt I/O order to the device */

                        if (CPX1 & cpx1_IOTIMER)                    /* if an I/O timeout occurred */
                            MICRO_ABORT (trap_SysHalt_IO_Timeout);  /*   then trap for a system halt */

                        if (iop_interrupt_request_set       /* if a hardware interrupt request is pending */
                          && STA & STATUS_I)                /*   and interrupts are enabled */
                            device = iop_poll ();           /*     then poll to see if it can be granted */

                        if (CPX1 & cpx1_EXTINTR) {          /* if a device is ready to interrupt */
                            CPX1 &= ~cpx1_EXTINTR;          /*   then handle it without exiting and restacking */

                            tprintf (cpu_dev, DEB_INSTR, BOV_FORMAT "  external interrupt\n",
                                     PBANK, P - 1 & R_MASK, device);

                            cpu_setup_irq_handler (irq_IXIT, device);   /* set up entry into the interrupt handler */
                            break;                                      /*   with the prior context still on the stack */
                            }
                        }

                    if (delta_q & STMK_D) {             /* if the dispatcher was interrupted */
                        CPX1 |= cpx1_DISPFLAG;          /*   then set the dispatcher-is-active flag */

                        new_q = ics_q;                  /* set the returning Q value */
                        new_sm = ics_q + 2 & R_MASK;    /*   and the returning SM value */

                        if (delta_qi & STMK_D           /* if the dispatcher is scheduled */
                          && disp_counter == 0) {       /*   and enabled */
                            cpu_start_dispatcher ();    /*     then restart it now */
                            break;                      /*       to redispatch */
                            }
                        }
                    }

                if (disp_active                                             /* if the dispatcher is launching a process */
                  || q_is_qi && ((delta_q & STMK_D) == 0                    /*   or a process was interrupted */
                                   || disp_counter != 0)) {                 /*     or the dispatcher is disabled */
                    cpu_read_memory (absolute, Q - 4 & LA_MASK, &stack_db); /*       then read the DB and stack bank */
                    cpu_read_memory (absolute, Q - 5 & LA_MASK, &SBANK);    /*         for the return process */

                    cpu_read_memory (absolute, Q - 7 & LA_MASK, &operand);  /* read the stack-DB-relative data limit */
                    DL = stack_db + operand & R_MASK;                       /*   and restore it */

                    cpu_read_memory (absolute, Q - 8 & LA_MASK, &operand);  /* read the stack-DB-relative stack limit */
                    Z = stack_db + operand & R_MASK;                        /*   and restore it */

                    cpu_write_memory (absolute, Q - 13, D16_UMAX);          /* set the trace flag to a non-zero value */

                    cpu_read_memory (absolute, Q - 6 & LA_MASK, &operand);  /* read the stack-DB-relative stack pointer */
                    Q = stack_db + operand - 2 & R_MASK;                    /*   and restore it */

                    cpu_read_memory (stack, Q, &delta_q);       /* read the relative frame pointer */

                    CPX1 &= ~(cpx1_ICSFLAG | cpx1_DISPFLAG);    /* clear the ICS and dispatcher-is-running flags */

                    new_sm = Q - 4 & R_MASK;                    /* set up the return TOS pointer */
                    new_q = Q - delta_q & R_MASK;               /*   and frame pointer */
                    }

                if (!disp_active                                /* if not launching a new process */
                  && !q_is_qi                                   /*   and returning */
                  && ((delta_q & STMK_D) == 0                   /*     to an interrupted interrupt handler */
                    || (delta_qi & STMK_D) == 0                 /*     or to the interrupted dispatcher */
                    || disp_counter != 0)) {                    /*     or to the dispatcher requesting a disabled redispatch */
                    new_sm = Q - 4 & R_MASK;                    /*       then set up the return TOS pointer */
                    new_q = Q - (delta_q & ~STMK_D) & R_MASK;   /*         and frame pointer */
                    }

                cpu_read_memory (stack, Q + 1 & LA_MASK, &DBANK);   /* restore the data bank */
                cpu_read_memory (stack, Q + 2 & LA_MASK, &DB);      /*   and data base values */

                cpu_exit_procedure (new_q, new_sm, 0);  /* set up the return code segment and stack */
                break;


            case 005:                                   /* these decode as LOCK in hardware */
            case 011:
            case 015:
                if (cpu_stop_flags & SS_UNDEF) {        /* if the undefined instruction stop is active */
                    status = STOP_UNIMPL;               /*   then stop the simulator */
                    break;
                    }

            /* fall through into the LOCK executor */

            case 001:                                   /* LOCK (none; MODE) */
                if (UNIT_CPU_MODEL == UNIT_SERIES_II) { /* if the CPU is a Series II */
                    status = STOP_UNIMPL;               /*   THIS INSTRUCTION IS NOT IMPLEMENTED YET */
                    }

                else                                    /* otherwise the instruction */
                    status = STOP_UNIMPL;               /*   is not implemented on this machine */
                break;


            case 004:                                   /* these decode as PCN in hardware */
            case 006:
            case 010:
            case 012:
            case 014:
            case 016:
                if (cpu_stop_flags & SS_UNDEF) {        /* if the undefined instruction stop is active */
                    status = STOP_UNIMPL;               /*   then stop the simulator */
                    break;
                    }

            /* fall through into the PCN executor */

            case 002:                                       /* PCN (none; STOV, MODE) */
                cpu_push ();                                /* push the stack down */

                if (UNIT_CPU_MODEL == UNIT_SERIES_II)       /* if the CPU is a Series II */
                    RA = PCN_SERIES_II;                     /*   then the CPU number is 1 */

                else if (UNIT_CPU_MODEL == UNIT_SERIES_III  /* otherwise if the CPU is a Series III */
                  || UNIT_CPU_MODEL == UNIT_SERIES_IIIX)    /*   or an extended Series III */
                    RA = PCN_SERIES_III;                    /*     then the CPU number is 2 */

                else                                        /* otherwise if it's anything else */
                    status = SCPE_IERR;                     /*   then there's a problem! */
                break;


            case 007:                                   /* these decode as UNLK in hardware */
            case 013:
            case 017:
                if (cpu_stop_flags & SS_UNDEF) {        /* if the undefined instruction stop is active */
                    status = STOP_UNIMPL;               /*   then stop the simulator */
                    break;
                    }                                   /* otherwise fall into the UNLK executor */

            /* fall through into the UNLK executor */

            case 003:                                   /* UNLK (none; MODE) */
                if (UNIT_CPU_MODEL == UNIT_SERIES_II) { /* if the CPU is a Series II */
                    status = STOP_UNIMPL;               /*   THIS INSTRUCTION IS NOT IMPLEMENTED YET */
                    }

                else                                    /* otherwise the instruction */
                    status = STOP_UNIMPL;               /*   is not implemented on this machine */
                break;
            }                                           /* all cases are handled */
        break;
    }                                                   /* all cases are handled */

return status;                                          /* return the execution status */
}


/* Execute a firmware extension instruction (subopcode 02, field 01).

   This routine is called to execute the DMUL, DDIV, or firmware extension
   instruction currently in the CIR.  Optional firmware extension instruction
   sets occupy instruction codes 020400-020777.  Two instructions in this range
   are base set instructions: DMUL (020570) and DDIV (020571).  The instruction
   formats are:

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

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 0   0   0   1 |    APL op     |  APL
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 0   0   1   1 |   COBOL op    |  COBOL
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 0   1   1   1 | 1   0   0 | x |  DMUL/DDIV
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   0 | 0   0   0   1 | 1 |  options  |  decimal op   |  EIS Decimal
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

   In hardware, optional instructions depend on the presence of the microcode
   that implements them.  All extension instructions initially check the
   "firmware option present" bit in CPX1 before branching to a calculated
   address for the extension microcode.  This bit is set by comparing jumpers
   W1-W8 on the CIR PCA to CIR bits 8-11.  If the "present" bit is clear, the
   firmware takes an Unimplemented Instruction trap.

   A machine with no options has all jumpers installed.  Removing jumpers sets
   the "firmware option present" bit for specific CIR ranges, as follows:

     Jumper  CIR 8-11    CIR Range    Option
     ------  --------  -------------  -----------------------------------------
       W1      0000    020400-020417  Extended Instruction Set (Floating Point)
       W2      0001    020420-020437  32105A APL Instruction Set
       W3      0010    020440-020457
       W4      0011    020460-020477  32234A COBOL II Extended Instruction Set
       W5      0100    020500-020517
       W6      0101    020520-020537
       W7      0110    020540-020557
       --      0111    020560-020577  Base Set (DMUL/DDIV)
       W8      1xxx    020600-020777  Extended Instruction Set (Decimal Arith)

   The range occupied by the base set has no jumper and is hardwired as
   "present".  In simulation, presence is determined by the settings of the CPU
   unit flags.


   Implementation notes:

    1. In simulation, the DDIV instruction must check for 32-bit overflow before
       dividing.  Otherwise, an Integer Overflow Exception may occur on the
       underlying machine instruction, aborting the simulator.
*/

static t_stat firmware_extension (void)
{
int32    dividend, divisor, quotient, remainder;
uint32   operation, suboperation;
t_int64  product;
t_uint64 check;
t_stat   status = SCPE_OK;

operation = FIRMEXTOP (CIR);                            /* get the operation from the instruction */

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

    case 000:                                           /* Extended Instruction Set (floating point) */
        if (cpu_unit [0].flags & UNIT_EIS)              /* if the firmware is installed */
            status = cpu_eis_fp_op ();                  /*   then call the EIS dispatcher */
        else                                            /* otherwise */
            status = STOP_UNIMPL;                       /*   the instruction range decodes as unimplemented */
        break;


    case 003:                                           /* COBOL II Extended Instruction Set */
        if (cpu_unit [0].flags & UNIT_CIS)              /* if the firmware is installed */
            status = cpu_cis_op ();                     /*   then call the CIS dispatcher */
        else                                            /* otherwise */
            status = STOP_UNIMPL;                       /*   the instruction range decodes as unimplemented */
        break;


    case 007:                                           /* base set */
        suboperation = FMEXSUBOP (CIR);                 /* get the suboperation from the instruction */

        switch (suboperation) {                         /* dispatch the suboperation */

            case 010:                                   /* DMUL (CCA, O; STUN, ARITH) */
                product =                               /* form a 64-bit product from a 32 x 32 multiplication */
                   INT32 (TO_DWORD (RD, RC)) * INT32 (TO_DWORD (RB, RA));

                check = (t_uint64) product & S32_OVFL_MASK;             /* check the top 33 bits and set overflow */
                SET_OVERFLOW (check != 0 && check != S32_OVFL_MASK);    /*   if they are not all zeros or all ones */

                cpu_pop ();                             /* pop two words */
                cpu_pop ();                             /*   from the stack */

                RB = UPPER_WORD (product);              /* move the 32-bit product */
                RA = LOWER_WORD (product);              /*   to the stack */

                SET_CCA (RB, RA);                       /* set the condition code */
                break;


            case 011:                                   /* DDIV (CCA, O; STUN, ARITH) */
                dividend = INT32 (TO_DWORD (RD, RC));   /* get the 32-bit signed dividend */
                divisor  = INT32 (TO_DWORD (RB, RA));   /*   and the 32-bit signed divisor from the stack */

                if (divisor == 0)                           /* if dividing by zero */
                    MICRO_ABORT (trap_Integer_Zero_Divide); /*   then trap or set the overflow flag */

                if (dividend == (int32) D32_SMIN && divisor == -1) {    /* if the division will overflow */
                    quotient  = dividend;                               /*   then set the quotient */
                    remainder = 0;                                      /*     and remainder explicitly */
                    SET_OVERFLOW (TRUE);                                /*       and trap or set overflow */
                    }

                else {                                  /* otherwise */
                    quotient  = dividend / divisor;     /*   form the 32-bit signed quotient */
                    remainder = dividend % divisor;     /*     and 32-bit signed remainder */
                    }

                RD = UPPER_WORD (quotient);             /* move the 32-bit quotient */
                RC = LOWER_WORD (quotient);             /*   to the stack */

                RB = UPPER_WORD (remainder);            /* move the 32-bit remainder */
                RA = LOWER_WORD (remainder);            /*   to the stack */

                SET_CCA (RD, RC);                       /* set the condition code */
                break;


            default:
                status = STOP_UNIMPL;                   /* the rest of the base set codes are unimplemented */
            }
        break;


    case 010:                                           /* Extended Instruction Set (decimal) */
    case 011:                                           /* Extended Instruction Set (decimal) */
    case 012:                                           /* Extended Instruction Set (decimal) */
    case 013:                                           /* Extended Instruction Set (decimal) */
    case 014:                                           /* Extended Instruction Set (decimal) */
    case 015:                                           /* Extended Instruction Set (decimal) */
    case 016:                                           /* Extended Instruction Set (decimal) */
    case 017:                                           /* Extended Instruction Set (decimal) */
        if (cpu_unit [0].flags & UNIT_EIS)              /* if the firmware is installed */
            status = cpu_eis_dec_op ();                 /*   then call the EIS dispatcher */
        else                                            /* otherwise */
            status = STOP_UNIMPL;                       /*   the instruction range decodes as unimplemented */
        break;


    default:
        status = STOP_UNIMPL;                           /* the firmware extension instruction is unimplemented */
    }

return status;                                          /* return the execution status */
}


/* Execute an I/O or control instruction (subopcode 03, field 00).

   This routine is called to execute the I/O or control instruction currently in
   the CIR.  The instruction formats are:

       0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   1 | 0   0   0   0 |  I/O opcode   |    K field    |  I/O
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
     | 0   0   1   1 | 0   0   0   0 |  cntl opcode  | 0   0 | cn op |  Control
     +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+



   Implementation notes:

    1. The PAUS instruction suspends instruction execution until an interrupt
       occurs.  It is intended to idle the CPU while suspending instruction
       fetches from memory to allow full-bandwidth access to the selector and
       multiplexer channels.

       If the simulation is stopped while a PAUS instruction is executing, it
       may be resumed after the PAUS by adding the -B switch to the STEP,
       CONTINUE, GO, or RUN command.  This corresponds in hardware to pressing
       the RUN/HALT switch twice.  Without the switch, execution will resume at
       the PAUS instruction.

       The CNTR register is set to the value of the SR register when the
       micromachine pauses.  This allows the SR value to be accessed by the
       diagnostics.  The top-of-stack registers are flushed to main memory when
       this occurs, which clears SR.  Resuming into a PAUS and then stopping the
       simulation again will show CNTR = 0.

    2. The SED instruction decodes bits 12-15, including the reserved bits
       12-14.  The canonical form has the reserved bits set to zero, and in
       hardware SED works correctly only if opcodes 030040 and 030041 are used.
       Opcodes 030042-030057 also decode as SED, but the status register is set
       improperly (the I bit is cleared, bits 12-15 are rotated right twice and
       then ORed into the status register).  If a non-canonical form is used in
       simulation, and the UNDEF stop is active, a simulation stop will occur.
       If the stop is bypassed or not set, then the instruction will execute as
       though the reserved bits were zero.

    3. The CMD instruction is simulated by assuming that the addressed module
       will send a return message to the CPU, causing a module interrupt.  If
       the module is the CPU, then the "return message" is the originating
       message, including whatever MOP was specified.  Memory modules return a
       no-operation MOP in response to a read or read/write ones MOP.  Sending a
       read/write ones MOP to a Series II memory module sets the addressed
       location to 177777 before the read value is returned.

    4. The module interrupt signal is qualified by the I-bit of the status
       register.  This is simulated by setting the cpx1_MODINTR bit in the CMD
       executor if the I-bit is set, by clearing the cpx1_MODINTR bit in the SED
       0 executor, and by setting the bit in the SED 1 executor if the MOD
       register is non-zero (indicating a pending module interrupt that has not
       been serviced).
*/

static t_stat io_control (void)
{
static const uint8 preadjustment [16] = {       /* stack preadjustment, indexed by operation */
    1, 0, 0, 2, 1, 0, 1, 1,                     /*   LST  PAUS SED  **** **** **** XEQ  SIO  */
    0, 1, 0, 1, 1, 2, 0, 0                      /*   RIO  WIO  TIO  CIO  CMD  SST  SIN  HALT */
    };

uint32  operation, address, offset, module;
HP_WORD operand, command, ics_q, delta_qi, disp_counter;
t_stat  status = SCPE_OK;

operation = IOCSUBOP (CIR);                             /* get the suboperation from the instruction */

PREADJUST_SR (preadjustment [operation]);               /* preadjust the TOS registers to the required number */

switch (operation) {                                    /* dispatch the I/O or control operation */

    case 000:                                           /* LST (CCA; STUN, STOV, MODE) */
        offset = IO_K (CIR);                            /* get the system table pointer offset */

        if (offset == 0) {                              /* if the specified offset is zero */
            cpu_read_memory (absolute,                  /*   then offset using the TOS */
                             RA + SGT_POINTER & LA_MASK,
                             &operand);
            cpu_pop ();                                 /* delete the TOS */
            }

        else                                            /* otherwise */
            cpu_read_memory (absolute,                  /*   use the specified offset */
                             offset + SGT_POINTER,      /*     which cannot overflow */
                             &operand);

        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        cpu_push ();                                    /* push the stack down */
        cpu_read_memory (absolute,                      /*   and read the table word onto the TOS */
                         X + operand + SGT_POINTER & LA_MASK,
                         &RA);

        SET_CCA (RA, 0);                                /* set the condition code */
      break;


    case 001:                                           /* PAUS (none; MODE) */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        CNTR = SR;                                      /* copy the stack register to the counter */
        cpu_flush ();                                   /*   and flush the TOS registers to memory */

        if (cpu_stop_flags & SS_PAUSE)                  /* if the pause stop is active */
            status = STOP_PAUS;                         /*   then stop the simulation */

        else if (!(cpu_stop_flags & SS_BYPASSED))       /* otherwise if stops are not bypassed */
            cpu_micro_state = paused;                   /*   then pause the micromachine */
        break;                                          /* otherwise bypass the pause */


    case 002:                                           /* SED (none; MODE) */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        if (IO_K (CIR) > 1                              /* if the K field is not 0 or 1 */
          && cpu_stop_flags & SS_UNDEF)                 /*   and the undefined instruction stop is active */
            status = STOP_UNIMPL;                       /*     then stop the simulator */

        else if (CIR & 1) {                             /* otherwise if bit 15 of the instruction is 1 */
            STA |= STATUS_I;                            /*   then enable interrupts */

            if (MOD != 0)                               /* if a module interrupt is pending */
                CPX1 |= cpx1_MODINTR;                   /*   then request it now */
            }

        else {                                          /* otherwise */
            STA &= ~STATUS_I;                           /*   disable interrupts */
            CPX1 &= ~cpx1_MODINTR;                      /*     and clear any indicated module interrupt */
            }
        break;


    case 003:                                           /* XCHD, PSDB, DISP, and PSEB */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        switch (CNTLOP (CIR)) {                         /* dispatch the control operation */

            case 000:                                   /* XCHD (none; STUN, MODE) */
                operand = RA;                           /* exchange the */
                RA = DB;                                /*   RA and */
                DB = operand;                           /*     DB values */

                operand = RB;                           /* exchange the */
                RB = DBANK;                             /*   RB and */
                DBANK = operand & BA_MASK;              /*     DBANK values */

                cpu_base_changed = TRUE;                /* this instruction changed the base registers */
                break;


            case 005:                                   /* these decode as PSDB in hardware */
            case 011:
            case 015:
                if (cpu_stop_flags & SS_UNDEF) {        /* if the undefined instruction stop is active */
                    status = STOP_UNIMPL;               /*   then stop the simulator */
                    break;
                    }

            /* fall through into the PSDB executor */

            case 001:                                   /* PSDB (none; MODE) */
                cpu_read_memory (absolute, ICS_Q,       /* read the ICS stack marker pointer value */
                                 &ics_q);

                cpu_read_memory (absolute, ics_q - 18 & LA_MASK,    /* read the dispatcher counter */
                                 &disp_counter);

                cpu_write_memory (absolute, ics_q - 18 & LA_MASK,   /*  and increment it */
                                  disp_counter + 1 & DV_MASK);
                break;


            case 004:                                   /* these decode as DISP in hardware */
            case 006:
            case 010:
            case 012:
            case 014:
            case 016:
                if (cpu_stop_flags & SS_UNDEF) {        /* if the undefined instruction stop is active */
                    status = STOP_UNIMPL;               /*   then stop the simulator */
                    break;
                    }

            /* fall through into the DISP executor */

            case 002:                                   /* DISP (CCx; MODE, CSTV, TRACE, ABS CST, BNDV) */
                cpu_read_memory (absolute, ICS_Q,       /* read the stack marker initial value */
                                 &ics_q);

                cpu_read_memory (absolute, ics_q - 18 & LA_MASK,    /* read the dispatcher counter */
                                 &disp_counter);

                cpu_write_memory (absolute, ics_q,                  /* set the dispatcher-is-scheduled flag */
                                  STMK_D);

                if (CPX1 & (cpx1_ICSFLAG | cpx1_DISPFLAG)   /* if the dispatcher is currently running */
                  || disp_counter > 0)                      /*   or the dispatcher is inhibited */
                    SET_CCG;                                /*     then set condition code "greater than" */

                else {                                      /* otherwise */
                    SET_CCE;                                /*   set condition code "equal" */
                    cpu_setup_ics_irq (irq_Dispatch, 0);    /*     and set up the ICS */

                    STA = STATUS_M;                         /* enter privileged mode with interrupts disabled */
                    cpu_start_dispatcher ();                /*   and start the dispatcher */
                    }
                break;


            case 007:                                   /* these decode as PSEB in hardware */
            case 013:
            case 017:
                if (cpu_stop_flags & SS_UNDEF) {        /* if the undefined instruction stop is active */
                    status = STOP_UNIMPL;               /*   then stop the simulator */
                    break;
                    }

            /* fall through into the PSEB executor */

            case 003:                                   /* PSEB (CCx; MODE, CSTV, TRACE, ABS CST, BNDV) */
                cpu_read_memory (absolute, ICS_Q,       /* read the stack marker initial value */
                                 &ics_q);

                cpu_read_memory (absolute, ics_q - 18 & LA_MASK,    /* read the dispatcher counter */
                                 &disp_counter);

                cpu_write_memory (absolute, ics_q - 18 & LA_MASK,   /*  and decrement it */
                                  disp_counter - 1 & DV_MASK);

                if (disp_counter == 0)                          /* if the dispatcher is already enabled */
                    MICRO_ABORT (trap_SysHalt_PSEB_Enabled);    /*   then trap for a system halt */

                else if (disp_counter > 1)              /* otherwise if the dispatcher is still inhibited */
                    SET_CCG;                            /*   then set condition code "greater than" */

                else if (CPX1 & cpx1_DISPFLAG) {            /* otherwise if the dispatcher is currently running */
                    cpu_write_memory (absolute, ics_q, 0);  /*   then clear any start dispatcher requests */
                    SET_CCG;                                /*     and set condition code "greater than" */
                    }

                else {                                  /* otherwise the dispatcher is ready to run */
                    cpu_read_memory (absolute, ics_q,   /* read the dispatcher's stack marker */
                                     &delta_qi);

                    if ((delta_qi & STMK_D) == 0        /* if the dispatcher is not scheduled */
                      || (CPX1 & cpx1_ICSFLAG))         /*   or if we're currently executing on the ICS */
                        SET_CCG;                        /*     then set condition code "greater than" */

                    else {                                      /* otherwise */
                        SET_CCE;                                /*   set condition code "equal" */
                        cpu_setup_ics_irq (irq_Dispatch, 0);    /*     and set up the ICS */

                        STA = STATUS_M;                         /* enter privileged mode with interrupts disabled */
                        cpu_start_dispatcher ();                /*   and start the dispatcher */
                        }
                    }
                break;
            }                                           /* all cases are handled */
        break;


    case 004:                                           /* SMSK and SCLK */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        if (CNTLOP (CIR) > 1                            /* if the reserved field is not 0 or 1 */
          && cpu_stop_flags & SS_UNDEF)                 /*   and the undefined instruction stop is active */
            status = STOP_UNIMPL;                       /*     then stop the simulator */

        else if (CNTLOP (CIR) == 0) {                   /* SMSK (CCx; STUN, MODE) */
            iop_direct_io (0, ioSMSK, RA);              /* send the "set mask" I/O order to all interfaces */

            if (CPX1 & cpx1_IOTIMER) {                  /* if an I/O timeout occurred */
                CPX1 &= ~cpx1_IOTIMER;                  /*   then clear the timer */
                SET_CCL;                                /*     and set condition code "greater than" */
                }

            else {                                          /* otherwise the mask was set */
                cpu_write_memory (absolute, INTERRUPT_MASK, /*   so write the interrupt mask to memory */
                                  RA);
                cpu_pop ();                                 /* pop the TOS */
                SET_CCE;                                    /*   and set condition code "equal" */
                }
            }

        else {                                          /* SCLK (none; STUN, MODE) */
            cpu_update_pclk ();                         /* update the process clock counter */
            PCLK = RA;                                  /*   and then set it */

            cpu_pop ();                                 /* delete the TOS */
            }
        break;


    case 005:                                           /* RMSK and RCLK */
        cpu_push ();                                    /* push the stack down */

        if (CNTLOP (CIR) > 1                            /* if the reserved field is not 0 or 1 */
          && cpu_stop_flags & SS_UNDEF)                 /*   and the undefined instruction stop is active */
            status = STOP_UNIMPL;                       /*     then stop the simulator */

        else if (CNTLOP (CIR) == 0)                     /* RMSK (STOV) */
            cpu_read_memory (absolute, INTERRUPT_MASK,  /* read the interrupt mask from memory */
                             &RA);

        else {                                          /* RCLK (none; STOV) */
            cpu_update_pclk ();                         /* update the process clock counter */
            RA = PCLK;                                  /*   and then read it */
            }
        break;


    case 006:                                           /* XEQ (none; BNDV) */
        address = SM + SR - IO_K (CIR) & LA_MASK;       /* get the address of the target instruction */

        if (address >= DB || PRIV) {                    /* if the address is not below DB or the mode is privileged */
            cpu_read_memory (stack, address, &NIR);     /*   then read the word at S - K into the NIR */

            P = P - 1 & R_MASK;                         /* decrement P so the instruction after XEQ is next */
            sim_interval = sim_interval + 1;            /*   but don't count the XEQ against a STEP count */
            }

        else                                            /* otherwise the address is below DB and not privileged */
            MICRO_ABORT (trap_Bounds_Violation);        /*   so trap with a bounds violation */
        break;


    case 007:                                           /* SIO (CCx; STUN, STOV, MODE) */
        operand = srw_io (ioSIO, SIO_OK);               /* send the SIO order to the device */

        if (operand)                                    /* if the start I/O operation succeeded */
            cpu_pop ();                                 /*   then delete the I/O program address */
        break;


    case 010:                                           /* RIO (CCX; STOV, MODE) */
        operand = srw_io (ioRIO, DIO_OK);               /* send the RIO order to the device */

        if (operand) {                                  /* if the read I/O operation succeeded */
            cpu_push ();                                /*   then push the stack down */
            RA = LOWER_WORD (operand);                  /*     and save the RIO response on the TOS */
            }
        break;


    case 011:                                           /* WIO (CCX; STUN, STOV, MODE) */
        operand = srw_io (ioWIO, DIO_OK);               /* send the WIO order to the device */

        if (operand)                                    /* if the write I/O operation succeeded */
            cpu_pop ();                                 /*   then delete the write value */
        break;


    case 012:                                           /* TIO (CCx; STOV, MODE) */
        operand = tcs_io (ioTIO);                       /* send the TIO order to the device */

        if (operand) {                                  /* if the test I/O operation succeeded */
            cpu_push ();                                /*   then push the stack down */
            RA = LOWER_WORD (operand);                  /*     and save the I/O response on the TOS */
            }
        break;


    case 013:                                           /* CIO (CCx; STUN, MODE) */
        operand = tcs_io (ioCIO);                       /* send the CIO order to the device */

        if (operand)                                    /* if the control operation succeeded */
            cpu_pop ();                                 /*   then delete the control value */
        break;


    case 014:                                           /* CMD (none; STUN, MODE) */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        address = SM + SR - IO_K (CIR) & LA_MASK;       /* get the location of the command word */
        cpu_read_memory (stack, address, &command);     /*   and read it from the stack or TOS registers */

        module = CMD_TO (command);                      /* get the addressed (TO) module number */

        if (module == MODULE_PORT_CNTLR                 /* if the selector channel port controller */
          || module >= MODULE_UNDEFINED)                /*   or an undefined module is addressed */
            CPX1 |= cpx1_CPUTIMER;                      /*     then a module timeout occurs */

        else if (module == MODULE_CPU)                  /* otherwise if the CPU is addressing itself */
            MOD = MOD_CPU_1                             /*   then set the MOD register */
                    | TO_MOD_FROM (module)              /*     FROM field to the TO address */
                    | TO_MOD_MOP (CMD_MOP (command));   /*       and include the MOP field value */

        else if (UNIT_CPU_MODEL == UNIT_SERIES_II)      /* otherwise if a Series II memory module is addressed */
            if (module >= MODULE_MEMORY_UPPER           /*   then if the upper module is addressed */
              && MEMSIZE < 128 * 1024)                  /*     but it's not present */
                CPX1 |= cpx1_CPUTIMER;                  /*       then it will not respond */

            else {                                                  /* otherwise the module address is valid */
                if (CMD_MOP (command) == MOP_READ_WRITE_ONES) {     /* if the operation is read/write ones */
                    address = TO_PA (module, RA);                   /*   then get the bank and address */
                    cpu_write_memory (absolute, address, D16_UMAX); /*     and set the addressed word to all one bits */
                    }

                MOD = MOD_CPU_1                         /* set the MOD register */
                        | TO_MOD_FROM (module)          /*   FROM field to the TO address */
                        | TO_MOD_MOP (MOP_NOP);         /*     and the module operation to NOP */
                }

        else                                            /* otherwise a Series III memory module is addressed */
            if (module >= MODULE_MEMORY_UPPER           /*   so if the upper module is addressed */
              && MEMSIZE < 512 * 1024)                  /*     but it's not present */
                CPX1 |= cpx1_CPUTIMER;                  /*       then it will not respond */

            else                                        /* otherwise the module address is valid */
                MOD = MOD_CPU_1                         /*   so set the MOD register */
                        | TO_MOD_FROM (module)          /*     FROM field to the TO address */
                        | TO_MOD_MOP (MOP_NOP);         /*       and the module operation to NOP */

        if (MOD != 0 && STA & STATUS_I)                 /* if a module interrupt is indicated and enabled */
            CPX1 |= cpx1_MODINTR;                       /*   then request it */

        cpu_pop ();                                     /* delete the TOS */
        break;


    case 015:                                           /* SST (none; STUN, MODE) */
        offset = IO_K (CIR);                            /* get the system table pointer offset */

        if (offset == 0) {                              /* if the specified offset is zero */
            cpu_read_memory (absolute,                  /*   then offset using the TOS */
                             RA + SGT_POINTER & LA_MASK,
                             &operand);
            cpu_pop ();                                 /* delete the TOS */
            }

        else                                            /* otherwise */
            cpu_read_memory (absolute,                  /*   use the specified offset */
                             offset + SGT_POINTER,      /*     which cannot overflow */
                             &operand);

        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        cpu_write_memory (absolute,                     /* write the TOS value into the table */
                          X + operand + SGT_POINTER & LA_MASK,
                          RA);

        cpu_pop ();                                     /* delete the TOS */
        break;


    case 016:                                           /* SIN (CCx; MODE) */
        tcs_io (ioSIN);                                 /* send the SIN order to the device */
        break;


    case 017:                                           /* HALT (none; MODE) */
        if (NPRV)                                       /* if the mode is not privileged */
            MICRO_ABORT (trap_Privilege_Violation);     /*   then abort with a privilege violation */

        CNTR = SR;                                      /* copy the stack register to the counter */
        cpu_flush ();                                   /*   and flush the TOS registers to memory */

        CPX2 &= ~cpx2_RUN;                              /* clear the run flip-flop */
        status = STOP_HALT;                             /*   and stop the simulator */
        break;
    }                                                   /* all cases are handled */

return status;                                          /* return the execution status */
}
