961 lines
32 KiB
C
961 lines
32 KiB
C
/* alpha_ev5_pal.c - Alpha EV5 PAL mode simulator
|
|
|
|
Copyright (c) 2003-2006, 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.
|
|
|
|
EV5 was the second generation Alpha CPU. It was a four-way, in order issue
|
|
CPU with onchip primary instruction and data caches, an onchip second level
|
|
cache, and support for an offchip third level cache. EV56 was a shrink, with
|
|
added support for byte and word operations. PCA56 was a version of EV56
|
|
without the onchip second level cache. PCA57 was a shrink of PCA56.
|
|
|
|
EV5 includes the usual five PALcode instructions:
|
|
|
|
HW_LD PALcode load
|
|
HW_ST PALcode store
|
|
HW_MTPR PALcode move to internal processor register
|
|
HW_MFPR PALcode move from internal processor register
|
|
HW_REI PALcode return
|
|
|
|
PALcode instructions can only be issued in PALmode, or in kernel mode
|
|
if the appropriate bit is set in ICSR.
|
|
|
|
EV5 implements 8 "PAL shadow" registers, which replace R8-R14, R25 in
|
|
PALmode without save/restore; and 24 "PAL temporary" registers.
|
|
|
|
Internal registers fall into three groups: IBox IPRs, MBox IPRs, and
|
|
PAL temporaries.
|
|
*/
|
|
|
|
#include "alpha_defs.h"
|
|
#include "alpha_ev5_defs.h"
|
|
|
|
t_uint64 ev5_palshad[PALSHAD_SIZE] = { 0 }; /* PAL shadow reg */
|
|
t_uint64 ev5_palsave[PALSHAD_SIZE] = { 0 }; /* PAL save main */
|
|
t_uint64 ev5_paltemp[PALTEMP_SIZE] = { 0 }; /* PAL temps */
|
|
t_uint64 ev5_palbase = 0; /* PALcode base */
|
|
t_uint64 ev5_excaddr = 0; /* exception address */
|
|
t_uint64 ev5_isr = 0; /* intr summary */
|
|
t_uint64 ev5_icsr = 0; /* IBox control */
|
|
t_uint64 ev5_itb_pte = 0; /* ITLB pte */
|
|
t_uint64 ev5_itb_pte_temp = 0; /* ITLB readout */
|
|
t_uint64 ev5_ivptbr = 0; /* IBox virt ptbl */
|
|
t_uint64 ev5_iva_form = 0; /* Ibox fmt'd VA */
|
|
t_uint64 ev5_va = 0; /* Mbox VA */
|
|
t_uint64 ev5_mvptbr = 0; /* Mbox virt ptbl */
|
|
t_uint64 ev5_va_form = 0; /* Mbox fmt'd VA */
|
|
t_uint64 ev5_dtb_pte = 0; /* DTLB pte */
|
|
t_uint64 ev5_dtb_pte_temp = 0; /* DTLB readout */
|
|
t_uint64 ev5_dc_test_tag = 0; /* Dcache test tag */
|
|
t_uint64 ev5_dc_test_tag_temp = 0; /* Dcache tag readout */
|
|
uint32 ev5_itb_tag = 0; /* ITLB tag (vpn) */
|
|
uint32 ev5_dtb_tag = 0; /* DTLB tag (vpn) */
|
|
uint32 ev5_icperr = 0; /* Icache par err */
|
|
uint32 ev5_mm_stat = 0; /* MBox fault code */
|
|
uint32 ev5_mcsr = 0; /* MBox control */
|
|
uint32 ev5_alt_mode = 0; /* MBox alt mode */
|
|
uint32 ev5_dc_mode = 0; /* Dcache mode */
|
|
uint32 ev5_dcperr = 0; /* Dcache par err */
|
|
uint32 ev5_dc_test_ctl = 0; /* Dcache test ctrl */
|
|
uint32 ev5_maf_mode = 0; /* MAF mode */
|
|
uint32 ev5_va_lock = 0; /* VA lock flag */
|
|
uint32 ev5_mchk = 0; /* machine check pin */
|
|
uint32 ev5_sli = 0; /* serial line intr */
|
|
uint32 ev5_crd = 0; /* corr read data pin */
|
|
uint32 ev5_pwrfl = 0; /* power fail pin */
|
|
uint32 ev5_ipl = 0; /* ipl */
|
|
uint32 ev5_sirr = 0; /* software int req */
|
|
uint32 ev5_astrr = 0; /* AST requests */
|
|
uint32 ev5_asten = 0; /* AST enables */
|
|
const uint32 ast_map[4] = { 0x1, 0x3, 0x7, 0xF };
|
|
|
|
t_stat ev5_palent (t_uint64 fpc, uint32 off);
|
|
t_stat ev5_palent_d (t_uint64 fpc, uint32 off, uint32 sta);
|
|
t_stat pal_proc_reset_hwre (DEVICE *dptr);
|
|
t_stat pal_proc_intr_ev5 (uint32 lvl);
|
|
uint32 pal_eval_intr_ev5 (uint32 flag);
|
|
|
|
extern t_uint64 R[32];
|
|
extern t_uint64 PC;
|
|
extern t_uint64 trap_mask;
|
|
extern t_uint64 p1;
|
|
extern uint32 ir;
|
|
extern uint32 vax_flag, lock_flag;
|
|
extern uint32 fpen;
|
|
extern uint32 pcc_h, pcc_l, pcc_enb;
|
|
extern uint32 trap_summ;
|
|
extern uint32 arch_mask;
|
|
extern uint32 pal_mode, pal_type;
|
|
extern uint32 int_req[IPL_HLVL];
|
|
extern uint32 itlb_cm, dtlb_cm;
|
|
extern uint32 itlb_asn, dtlb_asn;
|
|
extern uint32 itlb_spage, dtlb_spage;
|
|
extern jmp_buf save_env;
|
|
extern uint32 pal_type;
|
|
extern t_uint64 pcq[PCQ_SIZE]; /* PC queue */
|
|
extern int32 pcq_p; /* PC queue ptr */
|
|
|
|
extern int32 parse_reg (char *cptr);
|
|
|
|
/* EV5PAL data structures
|
|
|
|
ev5pal_dev device descriptor
|
|
ev5pal_unit unit
|
|
ev5pal_reg register list
|
|
*/
|
|
|
|
UNIT ev5pal_unit = { UDATA (NULL, 0, 0) };
|
|
|
|
REG ev5pal_reg[] = {
|
|
{ BRDATA (PALSHAD, ev5_palshad, 16, 64, PALSHAD_SIZE) },
|
|
{ BRDATA (PALSAVE, ev5_palsave, 16, 64, PALSHAD_SIZE) },
|
|
{ BRDATA (PALTEMP, ev5_paltemp, 16, 64, PALTEMP_SIZE) },
|
|
{ HRDATA (PALBASE, ev5_palbase, 64) },
|
|
{ HRDATA (EXCADDR, ev5_excaddr, 64) },
|
|
{ HRDATA (IPL, ev5_ipl, 5) },
|
|
{ HRDATA (SIRR, ev5_sirr, 15) },
|
|
{ HRDATA (ASTRR, ev5_astrr, 4) },
|
|
{ HRDATA (ASTEN, ev5_asten, 4) },
|
|
{ HRDATA (ISR, ev5_isr, 35) },
|
|
{ HRDATA (ICSR, ev5_icsr, 40) },
|
|
{ HRDATA (ITB_TAG, ev5_itb_tag, 32) },
|
|
{ HRDATA (ITB_PTE, ev5_itb_pte, 64) },
|
|
{ HRDATA (ITB_PTE_TEMP, ev5_itb_pte_temp, 64) },
|
|
{ HRDATA (IVA_FORM, ev5_iva_form, 64) },
|
|
{ HRDATA (IVPTBR, ev5_ivptbr, 64) },
|
|
{ HRDATA (ICPERR_STAT, ev5_icperr, 14) },
|
|
{ HRDATA (VA, ev5_va, 64) },
|
|
{ HRDATA (VA_FORM, ev5_va_form, 64) },
|
|
{ HRDATA (MVPTBR, ev5_mvptbr, 64) },
|
|
{ HRDATA (MM_STAT, ev5_mm_stat, 17) },
|
|
{ HRDATA (MCSR, ev5_mcsr, 6) },
|
|
{ HRDATA (DTB_TAG, ev5_dtb_tag, 32) },
|
|
{ HRDATA (DTB_PTE, ev5_dtb_pte, 64) },
|
|
{ HRDATA (DTB_PTE_TEMP, ev5_dtb_pte_temp, 64) },
|
|
{ HRDATA (DC_MODE, ev5_dc_mode, 4) },
|
|
{ HRDATA (DC_PERR_STAT, ev5_dcperr, 6) },
|
|
{ HRDATA (DC_TEST_CTL, ev5_dc_test_ctl, 13) },
|
|
{ HRDATA (DC_TEST_TAG, ev5_dc_test_tag, 39) },
|
|
{ HRDATA (DC_TEST_TAG_TEMP, ev5_dc_test_tag_temp, 39) },
|
|
{ HRDATA (MAF_MODE, ev5_maf_mode, 8) },
|
|
{ FLDATA (VA_LOCK, ev5_va_lock, 0) },
|
|
{ FLDATA (MCHK, ev5_mchk, 0) },
|
|
{ FLDATA (CRD, ev5_crd, 0) },
|
|
{ FLDATA (PWRFL, ev5_pwrfl, 0) },
|
|
{ FLDATA (SLI, ev5_sli, 0) },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE ev5pal_dev = {
|
|
"EV5PAL", &ev5pal_unit, ev5pal_reg, NULL,
|
|
1, 16, 1, 1, 16, 8,
|
|
NULL, NULL, &pal_proc_reset_hwre,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_DIS
|
|
};
|
|
|
|
/* EV5 interrupt dispatch - reached from top of instruction loop -
|
|
dispatch to PALcode */
|
|
|
|
t_stat pal_proc_intr (uint32 lvl)
|
|
{
|
|
return ev5_palent (PC, PALO_INTR);
|
|
}
|
|
|
|
/* EV5 trap dispatch - reached from bottom of instruction loop -
|
|
trap_mask and trap_summ are set up correctly - dispatch to PALcode */
|
|
|
|
t_stat pal_proc_trap (uint32 summ)
|
|
{
|
|
return ev5_palent (PC, PALO_TRAP);
|
|
}
|
|
|
|
/* EV5 exception dispatch - reached from ABORT handler -
|
|
set up any exception-specific registers - dispatch to PALcode */
|
|
|
|
t_stat pal_proc_excp (uint32 abval)
|
|
{
|
|
switch (abval) {
|
|
|
|
case EXC_RSVI: /* reserved instruction */
|
|
return ev5_palent (PC, PALO_RSVI);
|
|
|
|
case EXC_ALIGN: /* unaligned */
|
|
return ev5_palent (PC, PALO_ALGN);
|
|
|
|
case EXC_FPDIS: /* fp disabled */
|
|
return ev5_palent (PC, PALO_FDIS);
|
|
|
|
case EXC_FOX+EXC_R: /* FOR */
|
|
return ev5_palent_d (PC, PALO_DFLT, MM_STAT_FOR);
|
|
|
|
case EXC_FOX+EXC_W: /* FOW */
|
|
return ev5_palent_d (PC, PALO_DFLT, MM_STAT_FOR|MM_STAT_WR);
|
|
|
|
case EXC_BVA+EXC_E: /* instr bad VA */
|
|
case EXC_ACV+EXC_E: /* instr ACV */
|
|
ev5_itb_tag = VA_GETVPN (PC); /* fault VPN */
|
|
if (ev5_icsr & ICSR_NT) /* formatted addr */
|
|
ev5_iva_form = ev5_ivptbr | FMT_IVA_NT (PC);
|
|
else ev5_iva_form = ev5_ivptbr | FMT_IVA_VMS (PC);
|
|
return ev5_palent (PC, PALO_IACV);
|
|
|
|
case EXC_ACV+EXC_R: /* data read ACV */
|
|
return ev5_palent_d (PC, PALO_DFLT, MM_STAT_ACV);
|
|
|
|
case EXC_ACV+EXC_W: /* data write ACV */
|
|
return ev5_palent_d (PC, PALO_DFLT, MM_STAT_ACV|MM_STAT_WR);
|
|
|
|
case EXC_BVA+EXC_R: /* data read bad addr */
|
|
return ev5_palent_d (PC, PALO_DFLT, MM_STAT_BVA);
|
|
|
|
case EXC_BVA+EXC_W: /* data write bad addr */
|
|
return ev5_palent_d (PC, PALO_DFLT, MM_STAT_BVA|MM_STAT_WR);
|
|
|
|
case EXC_TBM + EXC_E: /* TLB miss */
|
|
ev5_itb_tag = VA_GETVPN (PC); /* fault VPN */
|
|
if (ev5_icsr & ICSR_NT) /* formatted addr */
|
|
ev5_iva_form = ev5_ivptbr | FMT_IVA_NT (PC);
|
|
else ev5_iva_form = ev5_ivptbr | FMT_IVA_VMS (PC);
|
|
return ev5_palent (PC, PALO_ITBM);
|
|
|
|
case EXC_TBM + EXC_R: /* data TB miss read */
|
|
if ((I_GETOP (ir) == HW_LD) && (ir & HW_LD_PTE))
|
|
return ev5_palent_d (PC, PALO_DTBM_D, MM_STAT_TBM);
|
|
return ev5_palent_d (PC, PALO_DTBM, MM_STAT_TBM);
|
|
|
|
case EXC_TBM + EXC_W: /* data TB miss write */
|
|
if ((I_GETOP (ir) == HW_LD) && (ir & HW_LD_PTE))
|
|
return ev5_palent_d (PC, PALO_DTBM_D, MM_STAT_TBM|MM_STAT_WR);
|
|
return ev5_palent_d (PC, PALO_DTBM, MM_STAT_TBM|MM_STAT_WR);
|
|
|
|
case EXC_RSVO: /* reserved operand */
|
|
case EXC_TNV+EXC_E: /* instr TNV */
|
|
case EXC_TNV+EXC_R: /* data read TNV */
|
|
case EXC_TNV+EXC_W: /* data write TNV */
|
|
case EXC_FOX+EXC_E: /* FOE */
|
|
return SCPE_IERR; /* should never get here */
|
|
|
|
default:
|
|
return STOP_INVABO;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* EV5 call PAL - reached from instruction decoder -
|
|
compute offset from function code - dispatch to PALcode */
|
|
|
|
t_stat pal_proc_inst (uint32 fnc)
|
|
{
|
|
uint32 off = (fnc & 0x3F) << 6;
|
|
|
|
if (fnc & 0x80) return ev5_palent (PC, PALO_CALLUNPR + off);
|
|
if (itlb_cm != MODE_K) ABORT (EXC_RSVI);
|
|
return ev5_palent (PC, PALO_CALLPR + off);
|
|
}
|
|
|
|
/* EV5 evaluate interrupts - returns highest outstanding
|
|
interrupt level about target ipl - plus nonmaskable flags
|
|
|
|
flag = 1: evaluate for real interrupt capability
|
|
flag = 0: evaluate as though IPL = 0, normal mode */
|
|
|
|
uint32 pal_eval_intr (uint32 flag)
|
|
{
|
|
uint32 i, req = 0;
|
|
uint32 lvl = flag? ev5_ipl: 0;
|
|
|
|
if (flag && pal_mode) return 0;
|
|
if (ev5_mchk) req = IPL_1F;
|
|
else if (ev5_crd && (ICSR & ICSR_CRDE)) req = IPL_CRD;
|
|
else if (ev5_pwrfl) req = IPL_PWRFL;
|
|
else if (int_req[3] && !(ICSR & ICSR_MSK3)) req = IPL_HMIN + 3;
|
|
else if (int_req[2] && !(ICSR & ICSR_MSK2)) req = IPL_HMIN + 2;
|
|
else if (int_req[1] && !(ICSR & ICSR_MSK1)) req = IPL_HMIN + 1;
|
|
else if (int_req[0] && !(ICSR & ICSR_MSK0)) req = IPL_HMIN + 0;
|
|
else if (ev5_sirr) {
|
|
for (i = IPL_SMAX; i > 0; i--) { /* check swre int */
|
|
if ((ev5_sirr >> (i - 1)) & 1) { /* req != 0? int */
|
|
req = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ((req < IPL_AST) && (ev5_astrr & ev5_asten & ast_map[itlb_cm]))
|
|
req = IPL_AST;
|
|
if (req <= lvl) req = 0;
|
|
if (ev5_sli && (ICSR & ICSR_SLE)) req = req | IPL_SLI;
|
|
if (ev5_isr & ISR_HALT) req = req | IPL_HALT;
|
|
return req;
|
|
}
|
|
|
|
/* EV5 enter PAL, data TLB miss/memory management flows -
|
|
set Mbox registers - dispatch to PALcode */
|
|
|
|
t_stat ev5_palent_d (t_uint64 fpc, uint32 off, uint32 sta)
|
|
{
|
|
if (!ev5_va_lock) { /* not locked? */
|
|
ev5_mm_stat = sta | /* merge IR<31:21> */
|
|
((ir >> (I_V_RA - MM_STAT_V_RA)) & MM_STAT_IMASK);
|
|
ev5_va = p1; /* fault address */
|
|
if (ev5_mcsr & MCSR_NT) /* formatted VA */
|
|
ev5_va_form = ev5_mvptbr | FMT_MVA_NT (p1);
|
|
else ev5_va_form = ev5_mvptbr | FMT_MVA_VMS (p1);
|
|
ev5_va_lock = 1; /* lock registers */
|
|
}
|
|
return ev5_palent (fpc, off);
|
|
}
|
|
|
|
/* EV5 enter PAL */
|
|
|
|
t_stat ev5_palent (t_uint64 fpc, uint32 off)
|
|
{
|
|
ev5_excaddr = fpc | pal_mode; /* save exc addr */
|
|
PCQ_ENTRY; /* save PC */
|
|
PC = ev5_palbase + off; /* new PC */
|
|
if (!pal_mode && (ev5_icsr & ICSR_SDE)) { /* entering PALmode? */
|
|
PAL_USE_SHADOW; /* swap in shadows */
|
|
}
|
|
pal_mode = 1; /* in PAL mode */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* PAL instructions */
|
|
|
|
/* 1B: HW_LD */
|
|
|
|
t_stat pal_1b (uint32 ir)
|
|
{
|
|
t_uint64 dsp, ea, res;
|
|
uint32 ra, rb, acc, mode;
|
|
|
|
if (!pal_mode && (!(itlb_cm == MODE_K) || /* pal mode, or kernel */
|
|
!(ev5_icsr & ICSR_HWE))) ABORT (EXC_RSVI); /* and enabled? */
|
|
ra = I_GETRA (ir); /* get ra */
|
|
rb = I_GETRB (ir); /* get rb */
|
|
dsp = HW_LD_GETDSP (ir); /* get displacement */
|
|
ea = (R[rb] + (SEXT_HW_LD_DSP (dsp))) & M64; /* eff address */
|
|
if (ir & HW_LD_V) { /* virtual? */
|
|
mode = (ir & HW_LD_ALT)? ev5_alt_mode: dtlb_cm; /* access mode */
|
|
acc = (ir & HW_LD_WCH)? ACC_W (mode): ACC_R (mode);
|
|
if (ir & HW_LD_Q) res = ReadAccQ (ea, acc); /* quad? */
|
|
else { /* long, sext */
|
|
res = ReadAccL (ea, acc);
|
|
res = SEXT_L_Q (res);
|
|
}
|
|
}
|
|
else if (ir & HW_LD_Q) R[ra] = ReadPQ (ea); /* physical, quad? */
|
|
else {
|
|
res = ReadPL (ea); /* long, sext */
|
|
res = SEXT_L_Q (res);
|
|
}
|
|
if (ir & HW_LD_LCK) lock_flag = 1; /* lock? set flag */
|
|
if (ra != 31) R[ra] = res; /* if not R31, store */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* 1F: HW_ST */
|
|
|
|
t_stat pal_1f (uint32 ir)
|
|
{
|
|
t_uint64 dsp, ea;
|
|
uint32 ra, rb, acc, mode;
|
|
|
|
if (!pal_mode && (!(itlb_cm == MODE_K) || /* pal mode, or kernel */
|
|
!(ev5_icsr & ICSR_HWE))) ABORT (EXC_RSVI); /* and enabled? */
|
|
ra = I_GETRA (ir); /* get ra */
|
|
rb = I_GETRB (ir); /* get rb */
|
|
dsp = HW_LD_GETDSP (ir); /* get displacement */
|
|
ea = (R[rb] + (SEXT_HW_LD_DSP (dsp))) & M64; /* eff address */
|
|
if ((ir & HW_LD_LCK) && !lock_flag) R[ra] = 0; /* lock fail? */
|
|
else {
|
|
if (ir & HW_LD_V) { /* virtual? */
|
|
mode = (ir & HW_LD_ALT)? ev5_alt_mode: dtlb_cm; /* access mode */
|
|
acc = ACC_W (mode);
|
|
if (ir & HW_LD_Q) WriteAccQ (ea, R[ra], acc); /* quad? */
|
|
else WriteAccL (ea, R[ra], acc); /* long */
|
|
}
|
|
else if (ir & HW_LD_Q) WritePQ (ea, R[ra]); /* physical, quad? */
|
|
else WritePL (ea, R[ra]); /* long */
|
|
if (ir & HW_LD_LCK) lock_flag = 0; /* unlock? clr flag */
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* 1E: HW_REI */
|
|
|
|
t_stat pal_1e (uint32 ir)
|
|
{
|
|
uint32 new_pal = ((uint32) ev5_excaddr) & 1;
|
|
|
|
if (!pal_mode && (!(itlb_cm == MODE_K) || /* pal mode, or kernel */
|
|
!(ev5_icsr & ICSR_HWE))) ABORT (EXC_RSVI); /* and enabled? */
|
|
PCQ_ENTRY;
|
|
PC = ev5_excaddr;
|
|
if (pal_mode && !new_pal && (ev5_icsr & ICSR_SDE)) { /* leaving PAL mode? */
|
|
PAL_USE_MAIN; /* swap out shadows */
|
|
}
|
|
pal_mode = new_pal;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* PAL move from processor registers */
|
|
|
|
t_stat pal_19 (uint32 ir)
|
|
{
|
|
t_uint64 res;
|
|
uint32 fnc, ra;
|
|
static const uint32 itbr_map_gh[4] = {
|
|
ITBR_PTE_GH0, ITBR_PTE_GH1, ITBR_PTE_GH2, ITBR_PTE_GH3 };
|
|
|
|
if (!pal_mode && (!(itlb_cm == MODE_K) || /* pal mode, or kernel */
|
|
!(ev5_icsr & ICSR_HWE))) ABORT (EXC_RSVI); /* and enabled? */
|
|
fnc = I_GETMDSP (ir);
|
|
ra = I_GETRA (ir);
|
|
switch (fnc) {
|
|
|
|
case ISR: /* intr summary */
|
|
res = ev5_isr | ((ev5_astrr & ev5_asten) << ISR_V_AST) |
|
|
((ev5_sirr & SIRR_M_SIRR) << ISR_V_SIRR) |
|
|
(int_req[0] && !(ev5_icsr & ICSR_MSK0)? ISR_IRQ0: 0) |
|
|
(int_req[1] && !(ev5_icsr & ICSR_MSK1)? ISR_IRQ1: 0) |
|
|
(int_req[2] && !(ev5_icsr & ICSR_MSK2)? ISR_IRQ2: 0) |
|
|
(int_req[3] && !(ev5_icsr & ICSR_MSK3)? ISR_IRQ3: 0);
|
|
if (ev5_astrr & ev5_asten & ast_map[itlb_cm]) res = res | ISR_ATR;
|
|
break;
|
|
|
|
case ITB_PTE:
|
|
res = itlb_read ();
|
|
ev5_itb_pte_temp = (res & PFN_MASK) |
|
|
((res & PTE_ASM)? ITBR_PTE_ASM: 0) |
|
|
((res & (PTE_KRE|PTE_ERE|PTE_SRE|PTE_URE)) <<
|
|
(ITBR_PTE_V_KRE - PTE_V_KRE)) |
|
|
itbr_map_gh[PTE_GETGH (res)];
|
|
res = 0;
|
|
break;
|
|
|
|
case ITB_ASN:
|
|
res = (itlb_asn & ITB_ASN_M_ASN) << ITB_ASN_V_ASN;
|
|
break;
|
|
|
|
case ITB_PTE_TEMP:
|
|
res = ev5_itb_pte_temp;
|
|
break;
|
|
|
|
case SIRR:
|
|
res = (ev5_sirr & SIRR_M_SIRR) << SIRR_V_SIRR;
|
|
break;
|
|
|
|
case ASTRR:
|
|
res = ev5_astrr & AST_MASK;
|
|
break;
|
|
|
|
case ASTEN:
|
|
res = ev5_asten & AST_MASK;
|
|
break;
|
|
|
|
case EXC_ADDR:
|
|
res = ev5_excaddr;
|
|
break;
|
|
|
|
case EXC_SUMM:
|
|
res = trap_summ & TRAP_SUMM_RW;
|
|
break;
|
|
|
|
case EXC_MASK:
|
|
res = trap_mask;
|
|
break;
|
|
|
|
case PAL_BASE:
|
|
res = ev5_palbase & PAL_BASE_RW;
|
|
break;
|
|
|
|
case ICM:
|
|
res = (itlb_cm & ICM_M_CM) << ICM_V_CM;
|
|
break;
|
|
|
|
case IPLR:
|
|
res = (ev5_ipl & IPLR_M_IPL) << IPLR_V_IPL;
|
|
break;
|
|
|
|
case INTID:
|
|
res = pal_eval_intr (0) & INTID_MASK;
|
|
break;
|
|
|
|
case IFAULT_VA_FORM:
|
|
res = ev5_iva_form;
|
|
break;
|
|
|
|
case IVPTBR:
|
|
res = ev5_ivptbr;
|
|
break;
|
|
|
|
case ICSR:
|
|
res = (ev5_icsr & ICSR_RW) | ICSR_MBO |
|
|
((itlb_spage & ICSR_M_SPE) << ICSR_V_SPE) |
|
|
((fpen & 1) << ICSR_V_FPE) |
|
|
((arch_mask & AMASK_BWX)? ICSR_BSE: 0);
|
|
break;
|
|
|
|
case PALTEMP+0x00: case PALTEMP+0x01: case PALTEMP+0x02: case PALTEMP+0x03:
|
|
case PALTEMP+0x04: case PALTEMP+0x05: case PALTEMP+0x06: case PALTEMP+0x07:
|
|
case PALTEMP+0x08: case PALTEMP+0x09: case PALTEMP+0x0A: case PALTEMP+0x0B:
|
|
case PALTEMP+0x0C: case PALTEMP+0x0D: case PALTEMP+0x0E: case PALTEMP+0x0F:
|
|
case PALTEMP+0x10: case PALTEMP+0x11: case PALTEMP+0x12: case PALTEMP+0x13:
|
|
case PALTEMP+0x14: case PALTEMP+0x15: case PALTEMP+0x16: case PALTEMP+0x17:
|
|
res = ev5_paltemp[fnc - PALTEMP];
|
|
break;
|
|
|
|
case DTB_PTE:
|
|
ev5_dtb_pte_temp = dtlb_read ();
|
|
res = 0;
|
|
break;
|
|
|
|
case DTB_PTE_TEMP:
|
|
res = ev5_dtb_pte_temp;
|
|
break;
|
|
|
|
case MM_STAT:
|
|
res = ev5_mm_stat;
|
|
break;
|
|
|
|
case VA:
|
|
res = ev5_va;
|
|
ev5_va_lock = 0;
|
|
break;
|
|
|
|
case VA_FORM:
|
|
res = ev5_va_form;
|
|
break;
|
|
|
|
case DC_PERR_STAT:
|
|
res = ev5_dcperr;
|
|
break;
|
|
|
|
case MCSR:
|
|
res = (ev5_mcsr & MCSR_RW) | ((dtlb_spage & MCSR_M_SPE) << MCSR_V_SPE);
|
|
break;
|
|
|
|
case DC_MODE:
|
|
res = ev5_dc_mode & DC_MODE_RW;
|
|
break;
|
|
|
|
case MAF_MODE:
|
|
res = ev5_maf_mode & MAF_MODE_RW;
|
|
break;
|
|
|
|
case CC:
|
|
res = (((t_uint64) pcc_h) << 32) | ((t_uint64) pcc_l);
|
|
break;
|
|
|
|
case DC_TEST_CTL:
|
|
res = ev5_dc_test_ctl & DC_TEST_CTL_RW;
|
|
break;
|
|
|
|
case DC_TEST_TAG:
|
|
// to be determined
|
|
res = 0;
|
|
break;
|
|
|
|
case DC_TEST_TAG_TEMP:
|
|
res = ev5_dc_test_tag_temp & DC_TEST_TAG_RW;
|
|
break;
|
|
|
|
default:
|
|
res = 0;
|
|
break;
|
|
}
|
|
|
|
if (ra != 31) R[ra] = res & M64;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* PAL move to processor registers */
|
|
|
|
t_stat pal_1d (uint32 ir)
|
|
{
|
|
uint32 fnc = I_GETMDSP (ir);
|
|
uint32 ra = I_GETRA (ir);
|
|
t_uint64 val = R[ra];
|
|
|
|
if (!pal_mode && (!(itlb_cm == MODE_K) || /* pal mode, or kernel */
|
|
!(ev5_icsr & ICSR_HWE))) ABORT (EXC_RSVI); /* and enabled? */
|
|
switch (fnc) {
|
|
|
|
case ITB_TAG:
|
|
ev5_itb_tag = VA_GETVPN (val);
|
|
break;
|
|
|
|
case ITB_PTE:
|
|
ev5_itb_pte = (val | PTE_V) & (PFN_MASK | ((t_uint64) (PTE_ASM | PTE_GH |
|
|
PTE_KRE | PTE_ERE | PTE_SRE | PTE_URE)));
|
|
itlb_load (ev5_itb_tag, ev5_itb_pte);
|
|
break;
|
|
|
|
case ITB_ASN:
|
|
itlb_set_asn ((((uint32) val) >> ITB_ASN_V_ASN) & ITB_ASN_M_ASN);
|
|
break;
|
|
|
|
case ITB_IA:
|
|
tlb_ia (TLB_CI | TLB_CA);
|
|
break;
|
|
|
|
case ITB_IAP:
|
|
tlb_ia (TLB_CI);
|
|
break;
|
|
|
|
case ITB_IS:
|
|
tlb_is (val, TLB_CI);
|
|
break;
|
|
|
|
case SIRR:
|
|
ev5_sirr = (((uint32) val) >> SIRR_V_SIRR) & SIRR_M_SIRR;
|
|
break;
|
|
|
|
case ASTRR:
|
|
ev5_astrr = ((uint32) val) & AST_MASK;
|
|
break;
|
|
|
|
case ASTEN:
|
|
ev5_asten = ((uint32) val) & AST_MASK;
|
|
break;
|
|
|
|
case EXC_ADDR:
|
|
ev5_excaddr = val;
|
|
break;
|
|
|
|
case EXC_SUMM:
|
|
trap_summ = 0;
|
|
trap_mask = 0;
|
|
break;
|
|
|
|
case PAL_BASE:
|
|
ev5_palbase = val & PAL_BASE_RW;
|
|
break;
|
|
|
|
case ICM:
|
|
itlb_set_cm ((((uint32) val) >> ICM_V_CM) & ICM_M_CM);
|
|
break;
|
|
|
|
case IPLR:
|
|
ev5_ipl = (((uint32) val) >> IPLR_V_IPL) & IPLR_M_IPL;
|
|
break;
|
|
|
|
case IVPTBR:
|
|
if (ev5_icsr & ICSR_NT) ev5_ivptbr = val & IVPTBR_NT;
|
|
else ev5_ivptbr = val & IVPTBR_VMS;
|
|
break;
|
|
|
|
case HWINT_CLR:
|
|
ev5_isr = ev5_isr & ~(val & HWINT_CLR_W1C);
|
|
break;
|
|
|
|
case ICSR:
|
|
if (pal_mode && ((val ^ ev5_icsr) & ICSR_SDE)) {
|
|
if (val & ICSR_SDE) { PAL_USE_SHADOW; }
|
|
else { PAL_USE_MAIN; }
|
|
}
|
|
ev5_icsr = val & ICSR_RW;
|
|
itlb_set_spage ((((uint32) val) >> ICSR_V_SPE) & ICSR_M_SPE);
|
|
fpen = (((uint32) val) >> ICSR_V_FPE) & 1;
|
|
if (val & ICSR_BSE) arch_mask = arch_mask | AMASK_BWX;
|
|
else arch_mask = arch_mask & ~AMASK_BWX;
|
|
break;
|
|
|
|
case ICPERR_STAT:
|
|
ev5_icperr = ev5_icperr & ~(((uint32) val) & ICPERR_W1C);
|
|
break;
|
|
|
|
case PALTEMP+0x00: case PALTEMP+0x01: case PALTEMP+0x02: case PALTEMP+0x03:
|
|
case PALTEMP+0x04: case PALTEMP+0x05: case PALTEMP+0x06: case PALTEMP+0x07:
|
|
case PALTEMP+0x08: case PALTEMP+0x09: case PALTEMP+0x0A: case PALTEMP+0x0B:
|
|
case PALTEMP+0x0C: case PALTEMP+0x0D: case PALTEMP+0x0E: case PALTEMP+0x0F:
|
|
case PALTEMP+0x10: case PALTEMP+0x11: case PALTEMP+0x12: case PALTEMP+0x13:
|
|
case PALTEMP+0x14: case PALTEMP+0x15: case PALTEMP+0x16: case PALTEMP+0x17:
|
|
ev5_paltemp[fnc - PALTEMP] = val;
|
|
break;
|
|
|
|
case DTB_ASN:
|
|
dtlb_set_asn (((uint32) (val >> DTB_ASN_V_ASN)) & DTB_ASN_M_ASN);
|
|
break;
|
|
|
|
case DTB_CM:
|
|
dtlb_set_cm (((uint32) (val >> ICM_V_CM)) & ICM_M_CM);
|
|
break;
|
|
|
|
case DTB_TAG:
|
|
ev5_dtb_tag = VA_GETVPN (val);
|
|
val = (val | PTE_V) & (PFN_MASK | ((t_uint64) (PTE_MASK & ~PTE_FOE)));
|
|
dtlb_load (ev5_dtb_tag, val);
|
|
break;
|
|
|
|
case DTB_PTE:
|
|
ev5_dtb_pte = val;
|
|
break;
|
|
|
|
case MVPTBR:
|
|
ev5_mvptbr = val & ~MVPTBR_MBZ;
|
|
break;
|
|
|
|
case DC_PERR_STAT:
|
|
ev5_dcperr = ev5_dcperr & ~(((uint32) val) & DC_PERR_W1C);
|
|
if ((ev5_dcperr & DC_PERR_W1C) == 0) ev5_dcperr = 0;
|
|
break;
|
|
|
|
case DTB_IA:
|
|
tlb_ia (TLB_CD | TLB_CA);
|
|
break;
|
|
|
|
case DTB_IAP:
|
|
tlb_ia (TLB_CD);
|
|
break;
|
|
|
|
case DTB_IS:
|
|
tlb_is (val, TLB_CD);
|
|
break;
|
|
|
|
case MCSR:
|
|
ev5_mcsr = ((uint32) val) & MCSR_RW;
|
|
dtlb_set_spage ((((uint32) val) >> MCSR_V_SPE) & MCSR_M_SPE);
|
|
if (ev5_mcsr & MCSR_NT) pal_type = PAL_NT;
|
|
break;
|
|
|
|
case DC_MODE:
|
|
ev5_dc_mode = ((uint32) val) & DC_MODE_RW;
|
|
break;
|
|
|
|
case MAF_MODE:
|
|
ev5_maf_mode = ((uint32) val) & MAF_MODE_RW;
|
|
break;
|
|
|
|
case CC:
|
|
pcc_h = (uint32) ((val >> 32) & M32);
|
|
break;
|
|
|
|
case CC_CTL:
|
|
pcc_l = ((uint32) val) & (M32 & ~CC_CTL_MBZ);
|
|
if (val & CC_CTL_ENB) pcc_enb = 1;
|
|
else pcc_enb = 0;
|
|
break;
|
|
|
|
case DC_TEST_CTL:
|
|
ev5_dc_test_ctl = ((uint32) val) & DC_TEST_CTL_RW;
|
|
break;
|
|
|
|
case DC_TEST_TAG:
|
|
ev5_dc_test_tag = val & DC_TEST_TAG_RW;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* EV5 PALcode reset */
|
|
|
|
t_stat pal_proc_reset_hwre (DEVICE *dptr)
|
|
{
|
|
ev5_palbase = 0;
|
|
ev5_mchk = 0;
|
|
ev5_pwrfl = 0;
|
|
ev5_crd = 0;
|
|
ev5_sli = 0;
|
|
itlb_set_cm (MODE_K);
|
|
itlb_set_asn (0);
|
|
itlb_set_spage (0);
|
|
dtlb_set_cm (MODE_K);
|
|
dtlb_set_asn (0);
|
|
dtlb_set_spage (0);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* EV5 PAL instruction print and parse routines */
|
|
|
|
static const char *pal_inam[] = {
|
|
"HW_MFPR", "HW_LD", "HW_MTPR", "HW_REI", "HW_ST", NULL
|
|
};
|
|
|
|
static const uint32 pal_ival[] = {
|
|
0x64000000, 0x6C000000, 0x74000000, 0x7BFF8000, 0x7C000000
|
|
};
|
|
|
|
struct pal_opt {
|
|
uint32 mask; /* bit mask */
|
|
char let; /* matching letter */
|
|
};
|
|
|
|
static struct pal_opt ld_st_opt[] = {
|
|
{ HW_LD_V, 'V' },
|
|
{ HW_LD_ALT, 'A' },
|
|
{ HW_LD_WCH, 'W' },
|
|
{ HW_LD_Q, 'Q' },
|
|
{ HW_LD_PTE, 'P' },
|
|
{ HW_LD_LCK, 'L' },
|
|
{ 0 }
|
|
};
|
|
|
|
static struct pal_opt rei_opt[] = {
|
|
{ HW_REI_S, 'S' },
|
|
{ 0 }
|
|
};
|
|
|
|
/* Print options for hardware PAL instruction */
|
|
|
|
void fprint_opt_ev5 (FILE *of, uint32 inst, struct pal_opt opt[])
|
|
{
|
|
uint32 i;
|
|
|
|
for (i = 0; opt[i].mask != 0; i++) {
|
|
if (inst & opt[i].mask) {
|
|
fprintf (of, "/%c", opt[i].let);
|
|
inst = inst & ~opt[i].mask;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Parse options for hardware PAL instruction */
|
|
|
|
char *parse_opt_ev5 (char *cptr, uint32 *val, struct pal_opt opt[])
|
|
{
|
|
uint32 i;
|
|
char *tptr, gbuf[CBUFSIZE];
|
|
|
|
if (*(cptr - 1) != '/') return cptr;
|
|
cptr = get_glyph (cptr - 1, tptr = gbuf, 0);
|
|
while (*tptr == '/') {
|
|
tptr++;
|
|
for (i = 0; opt[i].mask != 0; i++) {
|
|
if (*tptr == opt[i].let) {
|
|
*val = *val | opt[i].mask;
|
|
break;
|
|
}
|
|
}
|
|
if (opt[i].mask == 0) return NULL;
|
|
tptr++;
|
|
}
|
|
if (*tptr != 0) return NULL;
|
|
return cptr;
|
|
}
|
|
|
|
/* Print PAL hardware opcode symbolically */
|
|
|
|
t_stat fprint_pal_hwre (FILE *of, uint32 inst)
|
|
{
|
|
uint32 op, ra, rb;
|
|
|
|
op = I_GETOP (inst);
|
|
ra = I_GETRA (inst);
|
|
rb = I_GETRB (inst);
|
|
switch (op) {
|
|
|
|
case OP_PAL19: /* HW_MFPR */
|
|
case OP_PAL1D: /* HW_MTPR */
|
|
fputs ((op == OP_PAL19)? "HW_MFPR": "HW_MTPR", of);
|
|
fprintf (of, " R%d,%X", ra, inst & M16);
|
|
break;
|
|
|
|
case OP_PAL1B: /* HW_LD */
|
|
case OP_PAL1F: /* HW_ST */
|
|
fputs ((op == OP_PAL1B)? "HW_LD": "HW_ST", of);
|
|
fprint_opt_ev5 (of, inst, ld_st_opt);
|
|
fprintf (of, " R%d,%X", ra, inst & HW_LD_DSP);
|
|
if (rb != 31) fprintf (of, "(R%d)", rb);
|
|
break;
|
|
|
|
case OP_PAL1E: /* HW_REI */
|
|
fputs ("HW_REI", of);
|
|
fprint_opt_ev5 (of, inst, rei_opt);
|
|
break;
|
|
|
|
default:
|
|
return SCPE_ARG;
|
|
}
|
|
|
|
return -3;
|
|
}
|
|
|
|
/* Parse PAL hardware opcode symbolically */
|
|
|
|
t_stat parse_pal_hwre (char *cptr, t_value *inst)
|
|
{
|
|
uint32 i, d, val = 0;
|
|
int32 reg;
|
|
char *tptr, gbuf[CBUFSIZE];
|
|
t_stat r;
|
|
|
|
cptr = get_glyph (cptr, gbuf, '/');
|
|
for (i = 0; pal_inam[i] != NULL; i++) {
|
|
if (strcmp (gbuf, pal_inam[i]) == 0) val = pal_ival[i];
|
|
}
|
|
if (val == 0) return SCPE_ARG;
|
|
switch (I_GETOP (val)) {
|
|
|
|
case OP_PAL19: /* HW_MFPR */
|
|
case OP_PAL1D: /* HW_MTPR */
|
|
if (*(cptr - 1) == '/') return SCPE_ARG;
|
|
cptr = get_glyph (cptr, gbuf, ','); /* get reg */
|
|
if ((reg = parse_reg (gbuf)) < 0) return SCPE_ARG;
|
|
val = val | (reg << I_V_RA) | (reg << I_V_RB);
|
|
cptr = get_glyph (cptr, gbuf, 0); /* get ipr */
|
|
d = (uint32) get_uint (gbuf, 16, M16, &r);
|
|
if (r != SCPE_OK) return r;
|
|
val = val | d;
|
|
break;
|
|
|
|
case OP_PAL1B: /* HW_LD */
|
|
case OP_PAL1F: /* HW_ST */
|
|
cptr = parse_opt_ev5 (cptr, &val, ld_st_opt);
|
|
if (cptr == NULL) return SCPE_ARG;
|
|
cptr = get_glyph (cptr, gbuf, ','); /* get reg */
|
|
if ((reg = parse_reg (gbuf)) < 0) return SCPE_ARG;
|
|
val = val | (reg << I_V_RA);
|
|
cptr = get_glyph (cptr, gbuf, 0);
|
|
d = (uint32) strtotv (gbuf, &tptr, 16);
|
|
if ((gbuf == tptr) || (d > HW_LD_DSP)) return SCPE_ARG;
|
|
val = val | d;
|
|
if (*tptr == '(') {
|
|
tptr = get_glyph (tptr + 1, gbuf, ')');
|
|
if ((reg = parse_reg (gbuf)) < 0) return SCPE_ARG;
|
|
val = val | (reg << I_V_RB);
|
|
}
|
|
else val = val | (31 << I_V_RB);
|
|
break;
|
|
|
|
case OP_PAL1E: /* HW_REI */
|
|
cptr = parse_opt_ev5 (cptr, &val, rei_opt);
|
|
if (cptr == NULL) return SCPE_ARG;
|
|
break;
|
|
|
|
default:
|
|
return SCPE_ARG;
|
|
}
|
|
|
|
*inst = val;
|
|
if (*cptr != 0) return SCPE_ARG;
|
|
return -3;
|
|
}
|
|
|