/* 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; | |
} |