blob: 783dbc5e6ef7d4fd97904bc83d526a511bba235d [file] [log] [blame] [raw]
/* alpha_cpu.c: Alpha CPU simulator
Copyright (c) 2003-2017, 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.
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.
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
};
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, CONST char *cptr, void *desc);
t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat cpu_show_virt (FILE *of, UNIT *uptr, int32 val, CONST 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, CONST 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 },
{ HRDATA (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, CONST 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, CONST void *desc)
{
t_stat r;
CONST char *cptr = (CONST 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, CONST 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;
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, CONST void *desc)
{
int32 k, di, lnt;
CONST char *cptr = (CONST 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;
}