/* 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 (const 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 && (ev5_icsr & ICSR_CRDE)) req = IPL_CRD; | |
else if (ev5_pwrfl) req = IPL_PWRFL; | |
else if (int_req[3] && !(ev5_icsr & ICSR_MSK3)) req = IPL_HMIN + 3; | |
else if (int_req[2] && !(ev5_icsr & ICSR_MSK2)) req = IPL_HMIN + 2; | |
else if (int_req[1] && !(ev5_icsr & ICSR_MSK1)) req = IPL_HMIN + 1; | |
else if (int_req[0] && !(ev5_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 && (ev5_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 */ | |
CONST char *parse_opt_ev5 (CONST 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 (CONST char *cptr, t_value *inst) | |
{ | |
uint32 i, d, val = 0; | |
int32 reg; | |
CONST char *tptr; | |
char 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; | |
} | |