/* alpha_cpu.c: Alpha CPU simulator

   Copyright (c) 2003-2020, Robert M Supnik

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

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

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

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

   03-Mar-2020  RMS     Fixed DMAPEN register declaration (Mark Pizzolato)
   05-Oct-2017  RMS     Fixed reversed definitions of FTOIS, FTOIT (Maurice Marks)
   27-May-2017  RMS     Fixed MIN/MAXx4 iteration counts (Mark Pizzolato)
   26-May-2017  RMS     Fixed other reversed definitions in opcode 12
   28-Apr-2017  RMS     Fixed reversed definitions of INSQH, EXTQH (Maurice Marks)

   Alpha architecturally-defined CPU state:

   PC<63:0>                     program counter
   R[0:31]<63:0>                integer registers
   F[0:31]<63:0>                floating registers
   FPCR<63:0>                   floating point control register
                                (only left 32b are implemented)
   PCC<63:0>                    hardware cycle counter
   trap_summ<6:0>               arithmetic trap summary
   trap_mask<63:0>              arithmetic trap register mask
   lock_flag<varies>            load_locked flag
   vax_flag<0>                  VAX compatibility interrupt flag
   FEN<0>                       floating point enable flag

   The Alpha CPU privileged state is "soft" and varies significantly from
   operating system to operating system.  Alpha provides an intermediate layer
   of software (called PALcode) that implements the privileged state as well
   as a library of complex instruction functions.  PALcode implementations
   are chip specific and system specific, as well as OS specific.14

   Alpha memory management is also "soft" and supported a variety of mapping
   schemes.  VMS and Unix use a three level page table and directly expose
   the underlying 64b hardware PTE.  NT uses a condensed 32b PTE.

   All Alpha instructions are 32b wide.  There are five basic formats: PALcall,
   branch, memory reference, integer operate, and floating operate.

    3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
    1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           |                                                   |
   |   opcode  |                   PAL function                    | PAL
   |           |                                                   |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
    1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           |         |                                         |
   |   opcode  |    Ra   |           branch displacement           | branch
   |           |         |                                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
    1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           |         |         |                               |
   |   opcode  |    Ra   |    Rb   |      address displacement     | memory
   |           |         |         |                               | reference
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
    1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           |         |         |     | |             |         |
   |   opcode  |    Ra   |    Rb   |0 0 0|0|  function   |    Rc   | integer
   |           |         |         |     | |             |         | operate
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                         |               | |
                         |    literal    |1|
                         |               | |
                         +-+-+-+-+-+-+-+-+-+

    3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
    1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           |         |         |     |   |           |         |
   |   opcode  |    Ra   |    Rb   | trap|rnd|  function |    Rc   | floating
   |           |         |         |     |   |           |         | operate
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   Memory reference format is also used for some two-operand operates;
   the address displacement is the function code.

   This routine is the instruction decode routine for the Alpha.  It
   is called from the simulator control program to execute instructions
   in simulated memory, starting at the simulated PC.  It runs until an
   enabled exception is encountered.

   General notes:

   1. Traps and interrupts.  Variable trap_summ summarizes the outstanding
      trap requests (if any).  Variable intr_summ summarizes the outstanding
      interrupt requests (if any).

   2. Interrupt requests are maintained in the int_req array, one word per
      interrupt level, one bit per device.

   3. Adding I/O devices.  These modules must be modified:

        alpha_defs.h    add device address and interrupt definitions
        alpha_sys.c     add sim_devices table entry
*/

#include "alpha_defs.h"

#define UNIT_V_CONH     (UNIT_V_UF + 0)                 /* halt to console */
#define UNIT_V_MSIZE    (UNIT_V_UF + 1)
#define UNIT_CONH       (1 << UNIT_V_CONH)
#define UNIT_MSIZE      (1 << UNIT_V_MSIZE)

#define HIST_PC         0x2
#define HIST_MIN        64
#define HIST_MAX        (1 << 18)

typedef struct {
    t_uint64            pc;
    uint32              ir;
    uint32              filler;
    t_uint64            ra;
    t_uint64            rb;
    } InstHistory;

#define H_A             0x01
#define H_B             0x02
#define H_B_LIT         0x04
#define H_EA            0x08
#define H_EA_B          0x10
#define H_EA_L16        0x20
#define H_MRF           (H_A|H_B|H_EA)
#define H_BRA           (H_A|H_EA|H_EA_B)
#define H_IOP           (H_A|H_B|H_B_LIT)
#define H_FOP           (H_A|H_B)
#define H_PAL           (H_A|H_EA|H_EA_L16)
#define H_JMP           (H_A|H_B|H_EA|H_EA_L16)

t_uint64 *M = 0;                                        /* memory */
t_uint64 R[32];                                         /* integer reg */
t_uint64 FR[32];                                        /* floating reg */
t_uint64 PC;                                            /* PC, <1:0> MBZ */
uint32 pc_align = 0;                                    /* PC<1:0> */
t_uint64 trap_mask = 0;                                 /* trap reg mask */
uint32 trap_summ = 0;                                   /* trap summary */
uint32 fpcr = 0;                                        /* fp ctrl reg */
uint32 pcc_l = 0;                                       /* rpcc high */
uint32 pcc_h = 0;                                       /* rpcc low */
uint32 pcc_enb = 0;
uint32 arch_mask = AMASK_BWX | AMASK_PRC;               /* arch mask */
uint32 impl_ver = IMPLV_EV5;                            /* impl version */
uint32 lock_flag = 0;                                   /* load lock flag */
uint32 vax_flag = 0;                                    /* vax intr flag */
uint32 intr_summ = 0;                                   /* interrupt summary */
uint32 pal_mode = 1;                                    /* PAL mode */
uint32 pal_type = PAL_UNDF;                             /* PAL type */
uint32 dmapen = 0;                                      /* data mapping enable */
uint32 fpen = 0;                                        /* flt point enabled */
uint32 ir = 0;                                          /* instruction register */
t_uint64 p1 = 0;                                        /* exception parameter */
uint32 int_req[IPL_HLVL] = { 0 };                       /* interrupt requests */
REG *pcq_r = NULL;                                      /* PC queue reg ptr */
t_uint64 pcq[PCQ_SIZE] = { 0 };                         /* PC queue */
int32 pcq_p = 0;                                        /* PC queue ptr */
uint32 cpu_astop = 0;
uint32 hst_p = 0;                                       /* history pointer */
uint32 hst_lnt = 0;                                     /* history length */
InstHistory *hst = NULL;                                /* instruction history */
jmp_buf save_env;

const t_uint64 byte_mask[8] = {
    0x00000000000000FF, 0x000000000000FF00,
    0x0000000000FF0000, 0x00000000FF000000,
    0x000000FF00000000, 0x0000FF0000000000,
    0x00FF000000000000, 0xFF00000000000000
    };

const t_uint64 word_mask[4] = {
    0x000000000000FFFF, 0x00000000FFFF0000,
    0x0000FFFF00000000, 0xFFFF000000000000
    };

extern int32 sim_interval;
extern int32 sim_int_char;
extern FILE *sim_deb;
extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */

t_uint64 uemul64 (t_uint64 a, t_uint64 b, t_uint64 *hi);
t_uint64 byte_zap (t_uint64 op, uint32 mask);
t_stat cpu_reset (DEVICE *dptr);
t_stat cpu_boot (int32 unitno, DEVICE *dptr);
t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw);
t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw);
t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat cpu_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc);
t_stat cpu_fprint_one_inst (FILE *st, uint32 ir, t_uint64 pc, t_uint64 ra, t_uint64 rb);

extern t_uint64 op_ldf (t_uint64 op);
extern t_uint64 op_ldg (t_uint64 op);
extern t_uint64 op_lds (t_uint64 op);
extern t_uint64 op_stf (t_uint64 op);
extern t_uint64 op_stg (t_uint64 op);
extern t_uint64 op_sts (t_uint64 op);
extern t_uint64 vax_sqrt (uint32 ir, uint32 dp);
extern t_uint64 ieee_sqrt (uint32 ir, uint32 dp);
extern void vax_fop (uint32 ir);
extern void ieee_fop (uint32 ir);
extern t_stat pal_19 (uint32 ir);
extern t_stat pal_1b (uint32 ir);
extern t_stat pal_1d (uint32 ir);
extern t_stat pal_1e (uint32 ir);
extern t_stat pal_1f (uint32 ir);
extern t_uint64 trans_c (t_uint64 va);
extern t_stat cpu_show_tlb (FILE *of, UNIT *uptr, int32 val, void *desc);
extern uint32 pal_eval_intr (uint32 flag);
extern t_stat pal_proc_excp (uint32 type);
extern t_stat pal_proc_trap (uint32 type);
extern t_stat pal_proc_intr (uint32 type);
extern t_stat pal_proc_inst (uint32 fnc);
extern uint32 tlb_set_cm (int32 cm);

/* CPU data structures

   cpu_dev      CPU device descriptor
   cpu_unit     CPU unit
   cpu_reg      CPU register list
   cpu_mod      CPU modifier list
*/

UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, INITMEMSIZE) };

REG cpu_reg[] = {
    { HRDATA (PC, PC, 64), PV_LEFT },
    { HRDATA (PCALG, pc_align, 3) },
    { HRDATA (R0, R[0], 64) },
    { HRDATA (R1, R[1], 64) },
    { HRDATA (R2, R[2], 64) },
    { HRDATA (R3, R[3], 64) },
    { HRDATA (R4, R[4], 64) },
    { HRDATA (R5, R[5], 64) },
    { HRDATA (R6, R[6], 64) },
    { HRDATA (R7, R[7], 64) },
    { HRDATA (R8, R[8], 64) },
    { HRDATA (R9, R[9], 64) },
    { HRDATA (R10, R[10], 64) },
    { HRDATA (R11, R[11], 64) },
    { HRDATA (R12, R[12], 64) },
    { HRDATA (R13, R[13], 64) },
    { HRDATA (R14, R[14], 64) },
    { HRDATA (R15, R[15], 64) },
    { HRDATA (R16, R[16], 64) },
    { HRDATA (R17, R[17], 64) },
    { HRDATA (R18, R[18], 64) },
    { HRDATA (R19, R[19], 64) },
    { HRDATA (R20, R[20], 64) },
    { HRDATA (R21, R[21], 64) },
    { HRDATA (R22, R[22], 64) },
    { HRDATA (R23, R[23], 64) },
    { HRDATA (R24, R[24], 64) },
    { HRDATA (R25, R[25], 64) },
    { HRDATA (R26, R[26], 64) },
    { HRDATA (R27, R[27], 64) },
    { HRDATA (R28, R[28], 64) },
    { HRDATA (R29, R[29], 64) },
    { HRDATA (R30, R[30], 64) },
    { HRDATA (R31, R[31], 64), REG_RO },
    { HRDATA (F0, FR[0], 64) },
    { HRDATA (F1, FR[1], 64) },
    { HRDATA (F2, FR[2], 64) },
    { HRDATA (F3, FR[3], 64) },
    { HRDATA (F4, FR[4], 64) },
    { HRDATA (F5, FR[5], 64) },
    { HRDATA (F6, FR[6], 64) },
    { HRDATA (F7, FR[7], 64) },
    { HRDATA (F8, FR[8], 64) },
    { HRDATA (F9, FR[9], 64) },
    { HRDATA (F10, FR[10], 64) },
    { HRDATA (F11, FR[11], 64) },
    { HRDATA (F12, FR[12], 64) },
    { HRDATA (F13, FR[13], 64) },
    { HRDATA (F14, FR[14], 64) },
    { HRDATA (F15, FR[15], 64) },
    { HRDATA (F16, FR[16], 64) },
    { HRDATA (F17, FR[17], 64) },
    { HRDATA (F18, FR[18], 64) },
    { HRDATA (F19, FR[19], 64) },
    { HRDATA (F20, FR[20], 64) },
    { HRDATA (F21, FR[21], 64) },
    { HRDATA (F22, FR[22], 64) },
    { HRDATA (F23, FR[23], 64) },
    { HRDATA (F24, FR[24], 64) },
    { HRDATA (F25, FR[25], 64) },
    { HRDATA (F26, FR[26], 64) },
    { HRDATA (F27, FR[27], 64) },
    { HRDATA (F28, FR[28], 64) },
    { HRDATA (F29, FR[29], 64) },
    { HRDATA (F30, FR[30], 64) },
    { HRDATA (F31, FR[31], 64), REG_RO },
    { HRDATA (FPCR, fpcr, 32) },
    { FLDATA (FEN, fpen, 0) },
    { HRDATA (TRAPS, trap_summ, 8) },
    { HRDATA (TRAPM, trap_mask, 64) },
    { HRDATA (PCCH, pcc_h, 32) },
    { HRDATA (PCCL, pcc_l, 32) },
    { FLDATA (LOCK, lock_flag, 0) },
    { FLDATA (VAXF, vax_flag, 0) },
    { FLDATA (PALMODE, pal_mode, 0) },
    { HRDATA (PALTYPE, pal_type, 2), REG_HRO },
    { FLDATA (DMAPEN, dmapen, 0) },
    { HRDATA (AMASK, arch_mask, 13), REG_RO },
    { HRDATA (IMPLV, impl_ver, 2), REG_RO },
    { BRDATA (PCQ, pcq, 16, 32, PCQ_SIZE), REG_RO+REG_CIRC },
    { HRDATA (PCQP, pcq_p, 6), REG_HRO },
    { HRDATA (WRU, sim_int_char, 8) },
    { NULL }
    };

MTAB cpu_mod[] = {
    { UNIT_MSIZE, (1u << 25), NULL, "32M", &cpu_set_size },
    { UNIT_MSIZE, (1u << 26), NULL, "64M", &cpu_set_size },
    { UNIT_MSIZE, (1u << 27), NULL, "128M", &cpu_set_size },
    { UNIT_MSIZE, (1u << 28), NULL, "256M", &cpu_set_size },
    { UNIT_MSIZE, (1u << 29), NULL, "512M", &cpu_set_size },
    { UNIT_CONH, 0, "HALT to SIMH", "SIMHALT", NULL },
    { UNIT_CONH, UNIT_CONH, "HALT to console", "CONHALT", NULL },
    { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL,
      NULL, &cpu_show_virt },
    { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "ITLB", NULL,
      NULL, &cpu_show_tlb },
    { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 1, "DTLB", NULL,
      NULL, &cpu_show_tlb },
    { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
      &cpu_set_hist, &cpu_show_hist },
    { 0 }
    };

DEVICE cpu_dev = {
    "CPU", &cpu_unit, cpu_reg, cpu_mod,
    1, 16, 48, 8, 16, 64,
    &cpu_ex, &cpu_dep, &cpu_reset,
    &cpu_boot, NULL, NULL,
    NULL, DEV_DYNM|DEV_DEBUG, 0,
    NULL, &cpu_set_size, NULL
    };

t_stat sim_instr (void)
{
t_stat reason;
int abortval;
t_bool tracing;

PC = PC | pc_align;                                     /* put PC together */
abortval = setjmp (save_env);                           /* set abort hdlr */
if (abortval != 0) {                                    /* exception? */
    if (abortval < 0) {                                 /* SCP stop? */
        pcc_l = pcc_l & M32;
        pcq_r->qptr = pcq_p;                            /* update pc q ptr */
        pc_align = ((uint32) PC) & 3;                   /* separate PC<1:0> */
        PC = PC & 0xFFFFFFFFFFFFFFFC;
        return -abortval;
        }
    reason = pal_proc_excp (abortval);                  /* pal processing */
    }
else reason = 0;
tlb_set_cm (-1);                                        /* resync cm */
tracing = ((hst_lnt != 0) || DEBUG_PRS (cpu_dev));

intr_summ = pal_eval_intr (1);                          /* eval interrupts */

/* Main instruction loop */

while (reason == 0) {

    int32 i;
    uint32 op, ra, rb, rc, fnc, sc, s32, t32, sgn;
    t_int64 s1, s2, sr;
    t_uint64 ea, dsp, rbv, res, s64, t64;

    if (cpu_astop) {                                    /* debug stop? */
        cpu_astop = 0;                                  /* clear */
        reason = SCPE_STOP;                             /* stop simulation */
        break;
        }

    if (sim_interval <= 0) {                            /* chk clock queue */
        if (reason = sim_process_event ()) break;
        intr_summ = pal_eval_intr (1);                  /* eval interrupts */
        }

    if (intr_summ && !pal_mode) {                       /* interrupt pending? */
        reason = pal_proc_intr (intr_summ);             /* pal processing */
        intr_summ = pal_eval_intr (1);                  /* eval interrupts */
        continue;
        }

    if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) {      /* breakpoint? */
        reason = STOP_IBKPT;                            /* stop simulation */
        break;
        }

    sim_interval = sim_interval - 1;                    /* count instr */
    pcc_l = pcc_l + pcc_enb;
    ir = ReadI (PC);                                    /* get instruction */
    op = I_GETOP (ir);                                  /* get opcode */
    ra = I_GETRA (ir);                                  /* get ra */
    rb = I_GETRB (ir);                                  /* get rb */

    if (tracing) {                                      /* trace or history? */
        if (hst_lnt) {                                  /* history enabled? */
            hst_p = (hst_p + 1);                        /* next entry */
            if (hst_p >= hst_lnt) hst_p = 0;
            hst[hst_p].pc = PC | pc_align | HIST_PC;    /* save PC */
            hst[hst_p].ir = ir;                         /* save ir */
            hst[hst_p].ra = R[ra];                      /* save Ra */
            hst[hst_p].rb = R[rb];                      /* save Rb */
            }
        if (DEBUG_PRS (cpu_dev))                        /* trace enabled? */
            cpu_fprint_one_inst (sim_deb, ir, PC | pc_align, R[ra], R[rb]);
        }

    PC = (PC + 4) & M64;                                /* advance PC */
    switch (op) {

/* Memory reference instructions */

    case OP_LDA:                                        /* LDA */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            R[ra] = ea;
            }
        break;

    case OP_LDAH:                                       /* LDAH */
        if (ra != 31) {
            dsp = I_GETMDSP (ir) << 16;
            ea = (R[rb] + SEXT_L_Q (dsp)) & M64;
            R[ra] = ea;
            }
        break;

    case OP_LDBU:                                       /* LDBU */
        if (!(arch_mask & AMASK_BWX)) ABORT (EXC_RSVI); /* EV56 or later */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            R[ra] = ReadB (ea);
            }
        break;

    case OP_LDQ_U:                                      /* LDQ_U */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            R[ra] = ReadQ (ea & ~7);                    /* ignore ea<2:0> */
            }
        break;

    case OP_LDWU:                                       /* LDWU */
        if (!(arch_mask & AMASK_BWX)) ABORT (EXC_RSVI); /* EV56 or later */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            R[ra] = ReadW (ea);
            }
        break;

    case OP_STW:                                        /* STW */
        if (!(arch_mask & AMASK_BWX)) ABORT (EXC_RSVI); /* EV56 or later */
        dsp = I_GETMDSP (ir);
        ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
        WriteW (ea, R[ra]);
        break;

    case OP_STB:                                        /* STB */
        if (!(arch_mask & AMASK_BWX)) ABORT (EXC_RSVI); /* EV56 or later */
        dsp = I_GETMDSP (ir);
        ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
        WriteB (ea, R[ra]);
        break;

    case OP_STQ_U:                                      /* STQ_U */
        dsp = I_GETMDSP (ir);
        ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
        WriteQ (ea & ~7, R[ra]);                        /* ignore ea<2:0> */
        break;

    case OP_LDF:                                        /* LDF */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            FR[ra] = op_ldf (ReadL (ea));               /* swizzle bits */
            }
        break;

    case OP_LDG:                                        /* LDG */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            FR[ra] = op_ldg (ReadQ (ea));               /* swizzle bits */
            }
        break;

    case OP_LDS:                                        /* LDS */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            FR[ra] = op_lds (ReadL (ea));               /* swizzle bits */
            }
        break;

    case OP_LDT:                                        /* LDT */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            FR[ra] = ReadQ (ea);                        /* no swizzling needed */
            }
        break;

    case OP_STF:                                        /* STF */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        dsp = I_GETMDSP (ir);
        ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
        WriteL (ea, op_stf (FR[ra]));                   /* swizzle bits */
        break;

    case OP_STG:                                        /* STG */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        dsp = I_GETMDSP (ir);
        ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
        WriteQ (ea, op_stg (FR[ra]));                   /* swizzle bits */
        break;

    case OP_STS:                                        /* STS */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        dsp = I_GETMDSP (ir);
        ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
        WriteL (ea, op_sts (FR[ra]));                   /* swizzle bits */
        break;

    case OP_STT:                                        /* STT */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        dsp = I_GETMDSP (ir);
        ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
        WriteQ (ea, FR[ra]);                            /* no swizzling needed */
        break;

    case OP_LDL:                                        /* LDL */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            res = ReadL (ea);
            R[ra] = SEXT_L_Q (res);
            }
        break;

    case OP_LDQ:                                        /* LDQ */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            R[ra] = ReadQ (ea);
            }
        break;

    case OP_LDL_L:                                      /* LDL_L */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            res = ReadL (ea);
            R[ra] = SEXT_L_Q (res);
            lock_flag = 1;                              /* set lock flag */
            }
        break;

    case OP_LDQ_L:                                      /* LDQ_L */
        if (ra != 31) {
            dsp = I_GETMDSP (ir);
            ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
            R[ra] = ReadQ (ea);
            lock_flag = 1;                              /* set lock flag */
            }
        break;

    case OP_STL:                                        /* STL */
        dsp = I_GETMDSP (ir);
        ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
        WriteL (ea, R[ra]);
        break;

    case OP_STQ:                                        /* STQ */
        dsp = I_GETMDSP (ir);
        ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
        WriteQ (ea, R[ra]);
        break;

    case OP_STL_C:                                      /* STL_C */
        dsp = I_GETMDSP (ir);
        ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
        if (lock_flag) WriteL (ea, R[ra]);              /* unlocking? ok */
        else R[ra] = 0;                                 /* write fails */
        lock_flag = 0;                                  /* clear lock */
        break;

    case OP_STQ_C:                                      /* STQ_C */
        dsp = I_GETMDSP (ir);
        ea = (R[rb] + SEXT_MDSP (dsp)) & M64;
        if (lock_flag) WriteQ (ea, R[ra]);              /* unlocking? ok */
        else R[ra] = 0;                                 /* write fails */
        lock_flag = 0;                                  /* clear lock */
        break;

/* Control instructions */

    case OP_JMP:                                        /* JMP */
        PCQ_ENTRY;
        rbv = R[rb];                                    /* in case Ra = Rb */
        if (ra != 31) R[ra] = PC;                       /* save PC */
        PC = rbv;                                       /* jump */
        break;

    case OP_BR:                                         /* BR, BSR */
    case OP_BSR:
        PCQ_ENTRY;
        if (ra != 31) R[ra] = PC;                       /* save PC */
        dsp = I_GETBDSP (ir);
        PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;       /* branch */
        break;

    case OP_FBEQ:                                       /* FBEQ */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        if ((FR[ra] & ~FPR_SIGN) == 0) {                /* +0 or - 0? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_FBLT:                                       /* FBLT */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        if (FR[ra] > FPR_SIGN) {                        /* -0 to -n? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_FBLE:                                       /* FBLE */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        if ((FR[ra] & FPR_SIGN) || (FR[ra] == 0)) {     /* - or 0? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_FBNE:                                       /* FBNE */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        if ((FR[ra] & ~FPR_SIGN) != 0) {                /* not +0 or -0? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_FBGE:                                       /* FBGE */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        if (FR[ra] <= FPR_SIGN) {                       /* +0 to +n? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_FBGT:                                       /* FBGT */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        if (!(FR[ra] & FPR_SIGN) && (FR[ra] != 0)) {    /* not - and not 0? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_BLBC:                                       /* BLBC */
        if ((R[ra] & 1) == 0) {                         /* R<0> == 0? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_BEQ:                                        /* BEQ */
        if (R[ra] == 0) {                               /* R == 0? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_BLT:                                        /* BLT */
        if (R[ra] & Q_SIGN) {                           /* R<63> == 1? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_BLE:                                        /* BLE */
        if ((R[ra] == 0) || (R[ra] & Q_SIGN)) {         /* R == 0 || R<63> == 1? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_BLBS:                                       /* BLBS */
        if ((R[ra] & 1) != 0) {                         /* R<0> == 1? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_BNE:                                        /* BNE */
        if (R[ra] != 0) {                               /* R != 0? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_BGE:                                        /* BGE */
        if (!(R[ra] & Q_SIGN)) {                        /* R<63> == 0? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

    case OP_BGT:                                        /* BGT */
        if ((R[ra] != 0) && !(R[ra] & Q_SIGN)) {        /* R != 0 && R<63> == 0? */
            PCQ_ENTRY;
            dsp = I_GETBDSP (ir);
            PC = (PC + (SEXT_BDSP (dsp) << 2)) & M64;
            }
        break;

/* Integer arithmetic operates (10) */

    case OP_IALU:                                       /* integer arith opr */
        rc = I_GETRC (ir);                              /* get rc */
        if (ir & I_ILIT) rbv = I_GETLIT8 (ir);          /* literal? rbv = lit */
        else rbv = R[rb];                               /* no, rbv = R[rb] */
        fnc = I_GETIFNC (ir);                           /* get function */                              
        switch (fnc) {                                  /* case on function */

        case 0x00:                                      /* ADDL */
            res = SEXT_L_Q (R[ra] + rbv);
            break;

        case 0x02:                                      /* S4ADDL */
            res = SEXT_L_Q ((R[ra] << 2) + rbv);
            break;

        case 0x09:                                      /* SUBL */
            res = SEXT_L_Q (R[ra] - rbv);
            break;

        case 0x0B:                                      /* S4SUBL */
            res = SEXT_L_Q ((R[ra] << 2) - rbv);
            break;

        case 0x0F:                                      /* CMPBGE */
            for (i = 0, res = 0; i < 8; i++) {
                if ((R[ra] & byte_mask[i]) >= (rbv & byte_mask[i]))
                    res = res | ((t_uint64) 1u << i);
                }
            break;

        case 0x12:                                      /* S8ADDL */
            res = SEXT_L_Q ((R[ra] << 3) + rbv);
            break;

        case 0x1B:                                      /* S8SUBL */
            res = SEXT_L_Q ((R[ra] << 3) - rbv);
            break;

        case 0x1D:                                      /* CMPULT */
            res = (R[ra] < rbv);
            break;

        case 0x20:                                      /* ADDQ */
            res = R[ra] + rbv;
            break;

        case 0x22:                                      /* S4ADDQ */
            res = (R[ra] << 2) + rbv;
            break;

        case 0x29:                                      /* SUBQ */
            res = R[ra] - rbv;
            break;

        case 0x2B:                                      /* S4SUBQ */
            res = (R[ra] << 2) - rbv;
            break;

        case 0x2D:                                      /* CMPEQ */
            res = (R[ra] == rbv);
            break;

        case 0x32:                                      /* S8ADDQ */
            res = (R[ra] << 3) + rbv;
            break;

        case 0x3B:                                      /* S8SUBQ */
            res = (R[ra] << 3) - rbv;
            break;

        case 0x3D:                                      /* CMPULE */
            res = (R[ra] <= rbv);
            break;

        case 0x40:                                      /* ADDL/V */
            res = SEXT_L_Q (R[ra] + rbv);
            if (((~R[ra] ^ rbv) & (R[ra] ^ res)) & L_SIGN)
                arith_trap (TRAP_IOV, ir);
            break;

        case 0x49:                                      /* SUBL/V */
            res = SEXT_L_Q (R[ra] - rbv);
            if (((R[ra] ^ rbv) & (~rbv ^ res)) & L_SIGN)
                arith_trap (TRAP_IOV, ir);
            break;

        case 0x4D:                                      /* CMPLT */
            sgn = Q_GETSIGN (R[ra]);                    /* get Ra sign */
            if (sgn ^ Q_GETSIGN (rbv)) res = sgn;       /* signs diff? */
            else res = sgn ^ (R[ra] < rbv);
            break;

        case 0x60:                                      /* ADDQ/V */
            res = R[ra] + rbv;
            if (((~R[ra] ^ rbv) & (R[ra] ^ res)) & Q_SIGN)
                arith_trap (TRAP_IOV, ir);
            break;

        case 0x69:                                      /* SUBQ/V */
            res = R[ra] - rbv;
            if (((R[ra] ^ rbv) & (~rbv ^ res)) & Q_SIGN)
                arith_trap (TRAP_IOV, ir);
            break;

        case 0x6D:                                      /* CMPLE */
            if (R[ra] == rbv) res = 1;
            else {
                sgn = Q_GETSIGN (R[ra]);                /* get Ra sign */
                if (sgn ^ Q_GETSIGN (rbv)) res = sgn;   /* signs diff? */
                else res = sgn ^ (R[ra] < rbv);
                }

            break;
        default:
            res = R[rc];
            break;
            }

        if (rc != 31) R[rc] = res & M64;
        break;

/* Integer logical operates (11) */

    case OP_ILOG:                                       /* integer logic opr */
        rc = I_GETRC (ir);                              /* get rc */
        if (ir & I_ILIT) rbv = I_GETLIT8 (ir);          /* literal? rbv = lit */
        else rbv = R[rb];                               /* no, rbv = R[rb] */
        fnc = I_GETIFNC (ir);                           /* get function */                              
        switch (fnc) {                                  /* case on function */

        case 0x00:                                      /* AND */
            res = R[ra] & rbv;
            break;

        case 0x08:                                      /* BIC */
            res = R[ra] & ~rbv;
            break;

        case 0x14:                                      /* CMOVLBS */
            if ((R[ra] & 1) != 0) res = rbv;
            else res = R[rc];
            break;

        case 0x16:                                      /* CMOVLBC */
            if ((R[ra] & 1) == 0) res = rbv;
            else res = R[rc];
            break;

        case 0x20:                                      /* BIS */
            res = R[ra] | rbv;
            break;

        case 0x24:                                      /* CMOVEQ */
            if (R[ra] == 0) res = rbv;
            else res = R[rc];
            break;

        case 0x26:                                      /* CMOVNE */
            if (R[ra] != 0) res = rbv;
            else res = R[rc];
            break;

        case 0x28:                                      /* ORNOT */
            res = R[ra] | ~rbv;
            break;

        case 0x40:                                      /* XOR */
            res = R[ra] ^ rbv;
            break;

        case 0x44:                                      /* CMOVLT */
            if (R[ra] & Q_SIGN) res = rbv;
            else res = R[rc];
            break;

        case 0x46:                                      /* CMOVGE */
            if (!(R[ra] & Q_SIGN)) res = rbv;
            else res = R[rc];
            break;

        case 0x48:                                      /* EQV */
            res = R[ra] ^ ~rbv;
            break;

        case 0x61:                                      /* AMASK */
            res = rbv & ~arch_mask;
            break;

        case 0x64:                                      /* CMOVLE */
            if ((R[ra] & Q_SIGN) || (R[ra] == 0)) res = rbv;
            else res = R[rc];
            break;

        case 0x66:                                      /* CMOVGT */
            if (!(R[ra] & Q_SIGN) && (R[ra] != 0)) res = rbv;
            else res = R[rc];
            break;

        case 0x6C:                                      /* IMPLVER */
            res = impl_ver;
            break;

        default:
            res = R[rc];
            break;
            }

        if (rc != 31) R[rc] = res & M64;
        break;

/* Integer logical shifts (12) */

    case OP_ISHFT:                                      /* integer shifts */
        rc = I_GETRC (ir);                              /* get rc */
        if (ir & I_ILIT) rbv = I_GETLIT8 (ir);          /* literal? rbv = lit */
        else rbv = R[rb];                               /* no, rbv = R[rb] */
        fnc = I_GETIFNC (ir);                           /* get function */                              
        switch (fnc) {                                  /* case on function */

        case 0x02:                                      /* MSKBL */
            sc = ((uint32) rbv) & 7;
            res = byte_zap (R[ra], 0x1 << sc);
            break;

        case 0x06:                                      /* EXTBL */
            sc = (((uint32) rbv) << 3) & 0x3F;
            res = (R[ra] >> sc) & M8;
            break;

        case 0x0B:                                      /* INSBL */
            sc = (((uint32) rbv) << 3) & 0x3F;
            res = (R[ra] & M8) << sc;
            break;

        case 0x12:                                      /* MSKWL */
            sc = ((uint32) rbv) & 7;
            res = byte_zap (R[ra], 0x3 << sc);
            break;

        case 0x16:                                      /* EXTWL */
            sc = (((uint32) rbv) << 3) & 0x3F;
            res = (R[ra] >> sc) & M16;
            break;

        case 0x1B:                                      /* INSWL */
            sc = (((uint32) rbv) << 3) & 0x3F;
            res = (R[ra] & M16) << sc;
            break;

        case 0x22:                                      /* MSKLL */
            sc = ((uint32) rbv) & 7;
            res = byte_zap (R[ra], 0xF << sc);
            break;

        case 0x26:                                      /* EXTLL */
            sc = (((uint32) rbv) << 3) & 0x3F;
            res = (R[ra] >> sc) & M32;
            break;

        case 0x2B:                                      /* INSLL */
            sc = (((uint32) rbv) << 3) & 0x3F;
            res = (R[ra] & M32) << sc;
            break;

        case 0x30:                                      /* ZAP */
            res = byte_zap (R[ra], (uint32) rbv);
            break;

        case 0x31:                                      /* ZAPNOT */
            res = byte_zap (R[ra], ~((uint32) rbv));
            break;

        case 0x32:                                      /* MSKQL */
            sc = ((uint32) rbv) & 7;
            res = byte_zap (R[ra], 0xFF << sc);
            break;

        case 0x34:                                      /* SRL */
            sc = ((uint32) rbv) & 0x3F;
            res = R[ra] >> sc;
            break;

        case 0x36:                                      /* EXTQL */
            sc = (((uint32) rbv) << 3) & 0x3F;
            res = R[ra] >> sc;
            break;

        case 0x39:                                      /* SLL */
            sc = ((uint32) rbv) & 0x3F;
            res = R[ra] << sc;
            break;

        case 0x3B:                                      /* INSQL */
            sc = (((uint32) rbv) << 3) & 0x3F;
            res = R[ra] << sc;
            break;

        case 0x3C:                                      /* SRA */
            sc = ((uint32) rbv) & 0x3F;
            res = (R[ra] >> sc);
            if (sc && (R[ra] & Q_SIGN)) res = res |
                (((t_uint64) M64) << (64 - sc));
            break;

        case 0x52:                                      /* MSKWH */
            sc = 8 - (((uint32) rbv) & 7);
            res = byte_zap (R[ra], 0x3 >> sc);
            break;

        case 0x57:                                      /* INSWH */
            sc = (64 - (((uint32) rbv) << 3)) & 0x3F;
            res = (R[ra] & M16) >> sc;
            break;

        case 0x5A:                                      /* EXTWH */
            sc = (64 - (((uint32) rbv) << 3)) & 0x3F;
            res = (R[ra] << sc) & M16;
            break;

        case 0x62:                                      /* MSKLH */
            sc = 8 - (((uint32) rbv) & 7);
            res = byte_zap (R[ra], 0xF >> sc);
            break;

        case 0x67:                                      /* INSLH */
            sc = (64 - (((uint32) rbv) << 3)) & 0x3F;
            res = (R[ra] & M32) >> sc;
            break;

        case 0x6A:                                      /* EXTLH */
            sc = (64 - (((uint32) rbv) << 3)) & 0x3F;
            res = (R[ra] << sc) & M32;
            break;

        case 0x72:                                      /* MSKQH */
            sc = 8 - (((uint32) rbv) & 7);
            res = byte_zap (R[ra], 0xFF >> sc);
            break;

        case 0x77:                                      /* INSQH */
            sc = (64 - (((uint32) rbv) << 3)) & 0x3F;
            res = R[ra] >> sc;
            break;

        case 0x7A:                                      /* EXTQH */
            sc = (64 - (((uint32) rbv) << 3)) & 0x3F;
            res = R[ra] << sc;
            break;

        default:
            res = R[rc];
            break;
            }

        if (rc != 31) R[rc] = res & M64;
        break;

/* Integer multiply (13) */

    case OP_IMUL:                                       /* integer multiply */
        rc = I_GETRC (ir);                              /* get rc */
        if (ir & I_ILIT) rbv = I_GETLIT8 (ir);          /* literal? rbv = lit */
        else rbv = R[rb];                               /* no, rbv = R[rb] */
        fnc = I_GETIFNC (ir);                           /* get function */                              
        switch (fnc) {                                  /* case on function */

        case 0x00:                                      /* MULL */
            s1 = SEXT_L_Q (R[ra]);
            s2 = SEXT_L_Q (rbv);
            sr = s1 * s2;
            res = SEXT_L_Q (sr);
            break;

        case 0x20:                                      /* MULQ */
            res = uemul64 (R[ra], rbv, NULL);           /* low 64b invariant */
            break;                                      /* with sign/unsigned */

        case 0x30:                                      /* UMULH */
            uemul64 (R[ra], rbv, &res);
            break;

        case 0x40:                                      /* MULL/V */
            s1 = SEXT_L_Q (R[ra]);
            s2 = SEXT_L_Q (rbv);
            sr = s1 * s2;
            res = SEXT_L_Q (sr);
            if (((sr ^ res) & M64) != 0)                /* overflow? */
                arith_trap (TRAP_IOV, ir);
            break;

        case 0x60:                                      /* MULQ/V */
            res = uemul64 (R[ra], rbv, &t64);
            if (Q_GETSIGN(R[ra]))
                t64 = (t64 - rbv) & M64;
            if (Q_GETSIGN(rbv))
                t64 = (t64 - R[ra]) & M64;
            if (Q_GETSIGN (res)? (t64 != M64): (t64 != 0))
                arith_trap (TRAP_IOV, ir);
            break;

        default:
            res = R[rc];
            break;
            }

        if (rc != 31) R[rc] = res & M64;
        break;

/* FIX optional floating point set (14) */

    case OP_IFLT:                                       /* int to flt */
        if (!(arch_mask & AMASK_FIX)) ABORT (EXC_RSVI); /* EV56 or later */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        rc = I_GETRC (ir);                              /* get rc */
        fnc = I_GETFFNC (ir);                           /* get function */
        switch (fnc) {                                  /* case on function */

        case 0x04:                                      /* ITOFS */
            if (ir & (I_FRND|I_FTRP)) ABORT (EXC_RSVI);
            t32 = ((uint32) R[ra]) & M32;
            res = op_lds (t32);
            break;

        case 0x0A:                                      /* SQRTF */
            if (ir & I_F_VAXRSV) ABORT (EXC_RSVI);
            res = vax_sqrt (ir, DT_F);
            break;

        case 0x0B:                                      /* SQRTS */
            res = ieee_sqrt (ir, DT_S);
            break;

        case 0x14:                                      /* ITOFF */
            if (ir & (I_FRND|I_FTRP)) ABORT (EXC_RSVI);
            t32 = ((uint32) R[ra]) & M32;
            res = op_ldf (SWAP_VAXF (t32));
            break;

        case 0x24:                                      /* ITOFT */
            if (ir & (I_FRND|I_FTRP)) ABORT (EXC_RSVI);
            res = R[ra];
            break;

        case 0x2A:                                      /* SQRTG */
            if (ir & I_F_VAXRSV) ABORT (EXC_RSVI);
            res = vax_sqrt (ir, DT_G);
            break;

        case 0x2B:                                      /* SQRTT */
            res = ieee_sqrt (ir, DT_T);
            break;

        default:
            ABORT (EXC_RSVI);
            }

        if (rc != 31) FR[rc] = res & M64;
        break;

/* VAX and IEEE floating point operates - done externally */

    case OP_VAX:                                        /* VAX fp opr */
        if (ir & I_F_VAXRSV) ABORT (EXC_RSVI);          /* reserved */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        vax_fop (ir);
        break;

    case OP_IEEE:                                       /* IEEE fp opr */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        ieee_fop (ir);
        break;

/* Data type independent floating point (17) */

    case OP_FP:                                         /* other fp */
        if (fpen == 0) ABORT (EXC_FPDIS);               /* flt point disabled? */
        rc = I_GETRC (ir);                              /* get rc */
        fnc = I_GETFFNC (ir);                           /* get function */
        switch (fnc) {                                  /* case on function */

        case 0x10:                                      /* CVTLQ */
            res = ((FR[rb] >> 32) & 0xC0000000) | ((FR[rb] >> 29) & 0x3FFFFFFF);
            res = SEXT_L_Q (res);
            break;

        case 0x20:                                      /* CPYS */
            res = (FR[ra] & FPR_SIGN) | (FR[rb] & ~FPR_SIGN);
            break;

        case 0x21:                                      /* CPYSN */
            res = ((FR[ra] & FPR_SIGN) ^ FPR_SIGN) | (FR[rb] & ~FPR_SIGN);
            break;

        case 0x22:                                      /* CPYSE */
            res = (FR[ra] & (FPR_SIGN|FPR_EXP)) | (FR[rb] & ~(FPR_SIGN|FPR_EXP));
            break;

        case 0x24:                                      /* MT_FPCR */
            fpcr = ((uint32) (FR[ra] >> 32)) & ~FPCR_RAZ;
            res = FR[rc];
            break;

        case 0x25:                                      /* MF_FPCR */
            res = ((t_uint64) fpcr) << 32;
            break;

        case 0x2A:                                      /* FCMOVEQ */
            if ((FR[ra] & ~FPR_SIGN) == 0) res = FR[rb];
            else res = FR[rc];
            break;

        case 0x2B:                                      /* FCMOVNE */
            if ((FR[ra] & ~FPR_SIGN) != 0) res = FR[rb];
            else res = FR[rc];
            break;

        case 0x2C:                                      /* FCMOVLT */
            if (FR[ra] > FPR_SIGN) res = FR[rb];
            else res = FR[rc];
            break;

        case 0x2D:                                      /* FCMOVGE */
            if (FR[ra] <= FPR_SIGN) res = FR[rb];
            else res = FR[rc];
            break;

        case 0x2E:                                      /* FCMOVLE */
            if (FPR_GETSIGN (FR[ra]) || (FR[ra] == 0)) res = FR[rb];
            else res = FR[rc];
            break;

        case 0x2F:                                      /* FCMOVGT */
            if (!FPR_GETSIGN (FR[ra]) && (FR[ra] != 0)) res = FR[rb];
            else res = FR[rc];
            break;

        case 0x30:                                      /* CVTQL */
            res = ((FR[rb] & 0xC0000000) << 32) | ((FR[rb] & 0x3FFFFFFF) << 29);
            if (FPR_GETSIGN (FR[rb])?
                (FR[rb] < 0xFFFFFFFF80000000):
                (FR[rb] > 0x000000007FFFFFFF)) {
                fpcr = fpcr | FPCR_IOV | FPCR_INE | FPCR_SUM;
                if (ir & I_FTRP_V) arith_trap (TRAP_IOV, ir);
                }
            break;

        default:
            res = FR[rc];
            break;
            }

        if (rc != 31) FR[rc] = res & M64;
        break;

/* Barriers and misc (18)

   Alpha has a weak memory ordering model and an imprecise exception model;
   together, they require a wide variety of barrier instructions to guarantee
   memory coherency in multiprocessor systems, as well as backward compatible
   exception instruction semantics.

   The simulator is uniprocessor only, and has ordered memory accesses and
   precise exceptions.  Therefore, the barriers are all NOP's. */

    case OP_MISC:                                       /* misc */
        fnc = I_GETMDSP (ir);                           /* get function */
        switch (fnc) {                                  /* case on function */

        case 0xC000:                                    /* RPCC */
            pcc_l = pcc_l & M32;
            if (ra != 31) R[ra] = (((t_uint64) pcc_h) << 32) | ((t_uint64) pcc_l);
            break;

        case 0xE000:                                    /* RC */
            if (ra != 31) R[ra] = vax_flag;
            vax_flag = 0;
            break;

        case 0xF000:                                    /* RS */
            if (ra != 31) R[ra] = vax_flag;
            vax_flag = 1;
            break;

        default:
            break;
            }

        break;

/* Optional instruction sets (1C) */

    case OP_FLTI:                                       /* float to int */
        rc = I_GETRC (ir);                              /* get rc */
        if (ir & I_ILIT) rbv = I_GETLIT8 (ir);          /* literal? rbv = lit */
        else rbv = R[rb];                               /* no, rbv = R[rb] */
        fnc = I_GETIFNC (ir);                           /* get function */      
        switch (fnc) {                                  /* case on function */

        case 0x00:                                      /* SEXTB */
            if (!(arch_mask & AMASK_BWX)) ABORT (EXC_RSVI);
            res = SEXT_B_Q (rbv);
            break;

        case 0x01:                                      /* SEXTW */
            if (!(arch_mask & AMASK_BWX)) ABORT (EXC_RSVI);
            res = SEXT_W_Q (rbv);
            break;

        case 0x30:                                      /* CTPOP */
            if (!(arch_mask & AMASK_CIX)) ABORT (EXC_RSVI);
            for (res = 0; rbv != 0; res++) {
                rbv = rbv & ~(rbv & NEG_Q (rbv));
                }
            break;

        case 0x31:                                      /* PERR */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            for (i = 0, res = 0; i < 64; i = i + 8) {
                s32 = (uint32) (R[ra] >> i) & M8;
                t32 = (uint32) (rbv >> i) & M8;
                res = res + ((t_uint64) (s32 >= t32)? (s32 - t32): (t32 - s32));
                }
            break;

        case 0x32:                                      /* CTLZ */
            if (!(arch_mask & AMASK_CIX)) ABORT (EXC_RSVI);
            for (i = 0, res = 0; i < 64; i++) {
                if ((rbv >> (63 - i)) & 1) break;
                res = res + 1;
                }
            break;

        case 0x33:                                      /* CTTZ */
            if (!(arch_mask & AMASK_CIX)) ABORT (EXC_RSVI);
            for (i = 0, res = 0; i < 64; i++) {
                if ((rbv >> i) & 1) break;
                res = res + 1;
                }
            break;

        case 0x34:                                      /* UNPKBL */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            res = ((rbv & 0xFF00) << 24) | (rbv & 0xFF);
            break;

        case 0x35:                                      /* UNPKBW */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            res = ((rbv & 0xFF000000) << 24) | ((rbv & 0xFF0000) << 16) |
                ((rbv & 0xFF00) << 8) | (rbv & 0xFF);
            break;

        case 0x36:                                      /* PKWB */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            res = ((rbv >> 24) & 0xFF000000) | ((rbv >> 16) & 0xFF0000) |
                ((rbv >> 8) & 0xFF00) | (rbv & 0xFF);
            break;

        case 0x37:                                      /* PKLB */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            res = ((rbv >> 24) & 0xFF00) | (rbv & 0xFF);
            break;

        case 0x38:                                      /* MINSB8 */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            for (i = 0, res = 0; i < 8; i++) {
                s1 = SEXT_B_Q (R[ra] >> (i << 3));
                s2 = SEXT_B_Q (rbv >> (i << 3));
                res = res | (((s1 <= s2)? R[ra]: rbv) & byte_mask[i]);
                }
            break;

        case 0x39:                                      /* MINSW4 */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            for (i = 0, res = 0; i < 4; i++) {
                s1 = SEXT_W_Q (R[ra] >> (i << 4));
                s2 = SEXT_W_Q (rbv >> (i << 4));
                res = res | (((s1 <= s2)? R[ra]: rbv) & word_mask[i]);
                }
            break;

        case 0x3A:                                      /* MINUB8 */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            for (i = 0, res = 0; i < 8; i++) {
                s64 = R[ra] & byte_mask[i];
                t64 = rbv & byte_mask[i];
                res = res | ((s64 <= t64)? s64: t64);
                }
            break;

        case 0x3B:                                      /* MINUW4 */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            for (i = 0, res = 0; i < 4; i++) {
                s64 = R[ra] & word_mask[i];
                t64 = rbv & word_mask[i];
                res = res | ((s64 <= t64)? s64: t64);
                }
            break;

        case 0x3C:                                      /* MAXUB8 */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            for (i = 0, res = 0; i < 8; i++) {
                s64 = R[ra] & byte_mask[i];
                t64 = rbv & byte_mask[i];
                res = res | ((s64 >= t64)? s64: t64);
                }
            break;

        case 0x3D:                                      /* MAXUW4 */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            for (i = 0, res = 0; i < 4; i++) {
                s64 = R[ra] & word_mask[i];
                t64 = rbv & word_mask[i];
                res = res | ((s64 >= t64)? s64: t64);
                }
            break;

        case 0x3E:                                      /* MAXSB8 */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            for (i = 0, res = 0; i < 8; i++) {
                s1 = SEXT_B_Q (R[ra] >> (i << 3));
                s2 = SEXT_B_Q (rbv >> (i << 3));
                res = res | (((s1 >= s2)? R[ra]: rbv) & byte_mask[i]);
                }
            break;

        case 0x3F:                                      /* MAXSW4 */
            if (!(arch_mask & AMASK_MVI)) ABORT (EXC_RSVI);
            for (i = 0, res = 0; i < 4; i++) {
                s1 = SEXT_W_Q (R[ra] >> (i << 4));
                s2 = SEXT_W_Q (rbv >> (i << 4));
                res = res | (((s1 >= s2)? R[ra]: rbv) & word_mask[i]);
                }
            break;

        case 0x70:                                      /* FTOIT */
            if (!(arch_mask & AMASK_FIX)) ABORT (EXC_RSVI);
            if (fpen == 0) ABORT (EXC_FPDIS);           /* flt point disabled? */
            res = FR[ra];
            break;

        case 0x78:                                      /* FTOIS */
            if (!(arch_mask & AMASK_FIX)) ABORT (EXC_RSVI);
            if (fpen == 0) ABORT (EXC_FPDIS);           /* flt point disabled? */
            res = op_sts (FR[ra]);
            break;

        default:
            ABORT (EXC_RSVI);
            }

        if (rc != 31) R[rc] = res & M64;
        break;

/* PAL hardware functions */

    case OP_PAL19:
        reason = pal_19 (ir);
        intr_summ = pal_eval_intr (1);                  /* eval interrupts */
        break;

    case OP_PAL1B:
        reason = pal_1b (ir);
        intr_summ = pal_eval_intr (1);                  /* eval interrupts */
        break;

    case OP_PAL1D:
        reason = pal_1d (ir);
        intr_summ = pal_eval_intr (1);                  /* eval interrupts */
        break;

    case OP_PAL1E:
        reason = pal_1e (ir);
        intr_summ = pal_eval_intr (1);                  /* eval interrupts */
        break;

    case OP_PAL1F:
        reason = pal_1f (ir);
        intr_summ = pal_eval_intr (1);                  /* eval interrupts */
        break;

    case OP_PAL:                                        /* PAL code */
        fnc = I_GETPAL (ir);                            /* get function code */
        if ((fnc & 0x40) || (fnc >= 0xC0))              /* out of range? */
            ABORT (EXC_RSVI);
        reason = pal_proc_inst (fnc);                   /* processed externally */
        intr_summ = pal_eval_intr (1);                  /* eval interrupts */
        break;

    default:
        ABORT (EXC_RSVI);
        }                                               /* end case */
    if (trap_summ) {                                    /* any traps? */
        reason = pal_proc_trap (trap_summ);             /* process trap */
        trap_summ = 0;                                  /* clear trap reg */
        trap_mask = 0;
        intr_summ = pal_eval_intr (1);                  /* eval interrupts */
        }
    }                                                   /* end while */
pcc_l = pcc_l & M32;
pcq_r->qptr = pcq_p;                                    /* update pc q ptr */
pc_align = ((uint32) PC) & 3;                           /* separate PC<1:0> */
PC = PC & 0xFFFFFFFFFFFFFFFC;
return reason;
}

/* Utility routines */

/* Byte zap function */

t_uint64 byte_zap (t_uint64 op, uint32 m)
{
int32 i;

m = m & 0xFF;                                           /* 8 bit mask */
for (i = 0; m != 0; m = m >> 1, i++) {
    if (m & 1) op = op & ~byte_mask[i];
    }
return op;
}

/* 64b * 64b unsigned multiply */

t_uint64 uemul64 (t_uint64 a, t_uint64 b, t_uint64 *hi)
{
t_uint64 ahi, alo, bhi, blo, rhi, rmid1, rmid2, rlo;

ahi = (a >> 32) & M32;
alo = a & M32;
bhi = (b >> 32) & M32;
blo = b & M32;
rhi = ahi * bhi;
rmid1 = ahi * blo;
rmid2 = alo * bhi;
rlo = alo * blo;
rhi = rhi + ((rmid1 >> 32) & M32) + ((rmid2 >> 32) & M32);
rmid1 = (rmid1 << 32) & M64;
rmid2 = (rmid2 << 32) & M64;
rlo = (rlo + rmid1) & M64;
if (rlo < rmid1) rhi = rhi + 1;
rlo = (rlo + rmid2) & M64;
if (rlo < rmid2) rhi = rhi + 1;
if (hi) *hi = rhi & M64;
return rlo;
}

/* 64b / 64b unsigned fraction divide */

t_uint64 ufdiv64 (t_uint64 dvd, t_uint64 dvr, uint32 prec, uint32 *sticky)
{
t_uint64 quo;
uint32 i;

quo = 0;                                                /* clear quotient */
for (i = 0; (i < prec) && dvd; i++) {                   /* divide loop */
    quo = quo << 1;                                     /* shift quo */
    if (dvd >= dvr) {                                   /* div step ok? */
        dvd = dvd - dvr;                                /* subtract */
        quo = quo + 1;                                  /* quo bit = 1 */
        }
    dvd = dvd << 1;                                     /* shift divd */
    }
quo = quo << (UF_V_NM - i + 1);                         /* shift quo */
if (sticky) *sticky = (dvd? 1: 0);                      /* set sticky bit */
return quo;                                             /* return quotient */
}

/* Set arithmetic trap */

void arith_trap (uint32 mask, uint32 ir)
{
uint32 rc = I_GETRC (ir);

trap_summ = trap_summ | mask;
if (ir & I_FTRP_S) trap_summ = trap_summ | TRAP_SWC;
if ((mask & TRAP_IOV) == 0) rc = rc + 32;
trap_mask = trap_mask | ((t_uint64) 1u << rc);
return;
}

/* Reset */

t_stat cpu_reset (DEVICE *dptr)
{
R[31] = 0;
FR[31] = 0;
pal_mode = 1;
dmapen = 0;
fpen = 1;
vax_flag = 0;
lock_flag = 0;
trap_summ = 0;
trap_mask = 0;
if (M == NULL)
    M = (t_uint64 *) calloc (((uint32) MEMSIZE) >> 3, sizeof (t_uint64));
if (M == NULL)
    return SCPE_MEM;
pcq_r = find_reg ("PCQ", NULL, dptr);
if (pcq_r)
    pcq_r->qptr = 0;
else return SCPE_IERR;
sim_brk_types = sim_brk_dflt = SWMASK ('E');
return SCPE_OK;
}

/* Bootstrap */

t_stat cpu_boot (int32 unitno, DEVICE *dptr)
{
return SCPE_ARG;
}

/* Memory examine */

t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
if (vptr == NULL) return SCPE_ARG;
if (sw & SWMASK ('V') && dmapen) {
    addr = trans_c (addr);
    if (addr == M64)
        return STOP_MME;
    }
if (ADDR_IS_MEM (addr)) {
    *vptr = ReadPQ (addr);
    return SCPE_OK;
    }
return SCPE_NXM;
}

/* Memory deposit */

t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
if (sw & SWMASK ('V') && dmapen) {
    addr = trans_c (addr);
    if (addr == M64)
        return STOP_MME;
    }
if (ADDR_IS_MEM (addr)) {
    WritePQ (addr, val);
    return SCPE_OK;
    }
return SCPE_NXM;
}

/* Memory allocation */

t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc)
{
t_uint64 mc = 0;
uint32 i, clim;
t_uint64 *nM = NULL;

for (i = val; i < MEMSIZE; i = i + 8) mc = mc | M[i >> 3];
if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE))
    return SCPE_OK;
nM = (t_uint64 *) calloc (val >> 3, sizeof (t_uint64));
if (nM == NULL)
    return SCPE_MEM;
clim = (uint32) ((((uint32) val) < MEMSIZE)? val: MEMSIZE);
for (i = 0; i < clim; i = i + 8) nM[i >> 3] = M[i >>3];
free (M);
M = nM;
MEMSIZE = val;
return SCPE_OK;
}

/* Show virtual address */

t_stat cpu_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc)
{
t_stat r;
char *cptr = (char *) desc;
t_uint64 va, pa;

if (cptr) {
    DEVICE *dptr = find_dev_from_unit (uptr);
    if (dptr == NULL) return SCPE_IERR;
    va = get_uint (cptr, 16, M64, &r);
    if (r == SCPE_OK) {
        pa = trans_c (va);
        if (pa == M64) {
            fprintf (of, "Translation error\n");
            return SCPE_OK;
            }
        fputs ("Virtual ", of);
        fprint_val (of, va, 16, 64, PV_LEFT);
        fputs (" = physical ", of);
        fprint_val (of, pa, 16, 64, PV_LEFT);
        fputc ('\n', of);
        return SCPE_OK;
        }
    }
fprintf (of, "Invalid argument\n");
return SCPE_OK;
}

/* Set history */

t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc)
{
uint32 i, lnt;
t_stat r;

if (cptr == NULL) {
    for (i = 0; i < hst_lnt; i++) hst[i].pc = 0;
    hst_p = 0;
    return SCPE_OK;
    }
lnt = (uint32) get_uint (cptr, 10, HIST_MAX, &r);
if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG;
hst_p = 0;
if (hst_lnt) {
    free (hst);
    hst_lnt = 0;
    hst = NULL;
    }
if (lnt) {
    hst = (InstHistory *) calloc (lnt, sizeof (InstHistory));
    if (hst == NULL) return SCPE_MEM;
    hst_lnt = lnt;
    }
return SCPE_OK;
}

/* Print instruction trace */

t_stat cpu_fprint_one_inst (FILE *st, uint32 ir, t_uint64 pc, t_uint64 ra, t_uint64 rb)
{
uint32 op;
t_value sim_val;
extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val,
    UNIT *uptr, int32 sw);

static const int h_fmt[64] = {
    0,     0,     0,     0,     0,     0,     0,     0,
    H_MRF, H_MRF, H_MRF, H_MRF, H_MRF, H_MRF, H_MRF, H_MRF,
    H_IOP, H_IOP, H_IOP, H_IOP, H_FOP, H_FOP, H_FOP, H_FOP,
    0,     H_PAL, H_JMP, H_PAL, H_FOP, H_PAL, H_PAL, H_PAL,
    H_MRF, H_MRF, H_MRF, H_MRF, H_MRF, H_MRF, H_MRF, H_MRF,
    H_MRF, H_MRF, H_MRF, H_MRF, H_MRF, H_MRF, H_MRF, H_MRF,
    H_BRA, H_BRA, H_BRA, H_BRA, H_BRA, H_BRA, H_BRA, H_BRA,
    H_BRA, H_BRA, H_BRA, H_BRA, H_BRA, H_BRA, H_BRA, H_BRA
    };

pc = pc & ~HIST_PC;
fprint_val (st, pc, 16, 64, PV_RZRO);
fputc (' ', st);
op = I_GETOP (ir);                                      /* get opcode */
if (h_fmt[op] & H_A) fprint_val (st, ra, 16, 64, PV_RZRO);
else fputs ("                ", st);
fputc (' ', st);
if (h_fmt[op] & H_B) {                                  /* Rb? */
    t_uint64 rbv;
    if ((h_fmt[op] & H_B_LIT) && (ir & I_ILIT))
        rbv = I_GETLIT8 (ir);                           /* literal? rbv = lit */
    else rbv = rb;                                      /* no, rbv = R[rb] */
    fprint_val (st, rbv, 16, 64, PV_RZRO);
    }
else fputs ("                ", st);
fputc (' ', st);
if (h_fmt[op] & H_EA) {                                 /* ea? */
    t_uint64 ea;
    if (h_fmt[op] & H_EA_L16) ea = ir & M16;
    else if (h_fmt[op] & H_EA_B)
        ea = (pc + (SEXT_BDSP (I_GETBDSP (ir)) << 2)) & M64;
    else ea = (rb + SEXT_MDSP (I_GETMDSP (ir))) & M64;
    fprint_val (st, ea, 16, 64, PV_RZRO);
    }
else fputs ("                ", st);
fputc (' ', st);
if (pc & 4)
    sim_val = ((t_uint64) ir) << 32;
else sim_val = ir;
if ((fprint_sym (st, pc & ~03, &sim_val, &cpu_unit, SWMASK ('M'))) > 0)
    fprintf (st, "(undefined) %08X", ir);
fputc ('\n', st);                                       /* end line */
return SCPE_OK;
}

/* Show history */

t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc)
{
int32 k, di, lnt;
char *cptr = (char *) desc;
t_stat r;
InstHistory *h;

if (hst_lnt == 0) return SCPE_NOFNC;                    /* enabled? */
if (cptr) {
    lnt = (int32) get_uint (cptr, 10, hst_lnt, &r);
    if ((r != SCPE_OK) || (lnt == 0))
        return SCPE_ARG;
    }
else lnt = hst_lnt;
di = hst_p - lnt;                                       /* work forward */
if (di < 0) di = di + hst_lnt;
fprintf (st, "PC               Ra               Rb               IR\n\n");
for (k = 0; k < lnt; k++) {                             /* print specified */
    h = &hst[(++di) % hst_lnt];                         /* entry pointer */
    if (h->pc & HIST_PC) {                              /* instruction? */
        cpu_fprint_one_inst (st, h->ir, h->pc, h->ra, h->rb);
        }                                               /* end if */
    }                                                   /* end for */
return SCPE_OK;
}
