| /* alpha_pal_vms.c - Alpha VMS PAL code simulator | |
| Copyright (c) 2003-2005, 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. | |
| This module contains the PALcode implementation for Alpha VMS, except for | |
| the console, which is always done in hardware mode. | |
| Alpha VMS requires a complex privileged state, modelled after the VAX: | |
| PS<12:0> processor status | |
| IPL<4:0> interrupt level - in base | |
| VMM<0> virtual machine mode | |
| CM<1:0> current mode - in base | |
| IP<0> interrupt in progress | |
| SW<1:0> software controlled | |
| KSP<63:0> kernel stack pointer | |
| ESP<63:0> executive stack pointer | |
| SSP<63:0> supervisor stack pointer | |
| USP<63:0> user stack pointer | |
| SSC<63:0> system cycle counter | |
| PCBB<63:0> process control block base | |
| SCBB<63:0> system control block base | |
| PTBR<63:0> page table base | |
| VTBR<63:0> virtual page table base | |
| VIRBND<63:0> virtual address boundary | |
| SYSPTBR<63:0> system page table base register | |
| PRBR<63:0> processor base register | |
| THREAD<63:0> thread-unique value | |
| SIRR<15:1> software interrupt requests | |
| ASTEN<3:0> AST enables | |
| ASTRQ<3:0> AST requests | |
| FEN<0> floating enable | |
| DATFX<0> data alignment trap enable | |
| Note that some of this state exists in the hardware implementations and | |
| so is declared in the base CPU. | |
| */ | |
| #include "alpha_defs.h" | |
| /* Alignment table */ | |
| #define ALG_W 1 /* word inst */ | |
| #define ALG_L 2 /* long inst */ | |
| #define ALG_Q 3 /* quad inst */ | |
| #define ALG_ST 0x10 /* store */ | |
| #define ALG_INV -1 /* invalid inst */ | |
| #define ALG_ERR 0 /* internal error */ | |
| #define ALG_GETLNT(x) ((x) & 3) | |
| #define GET_PSV ((vms_ipl << PSV_V_IPL) | (vms_cm << PSV_V_CM) | \ | |
| (vms_ps & PSV_MASK)) | |
| #define AST_TST(l) (((l) < IPL_AST) && (vms_asten & vms_astsr & ast_map[vms_cm])) | |
| #define MOST_PRIV(m1,m2) (((m1) < (m2))? (m1): (m2)) | |
| #define ksp vms_stkp[MODE_K] | |
| #define esp vms_stkp[MODE_E] | |
| #define ssp vms_stkp[MODE_S] | |
| #define usp vms_stkp[MODE_U] | |
| // kludge for debugging... | |
| #define io_get_vec(x) 0 | |
| t_uint64 vms_ptbr = 0; /* page table base */ | |
| t_uint64 vms_vtbr = 0; /* virt page table base */ | |
| t_uint64 vms_virbnd = M64; /* virtual boundary */ | |
| t_uint64 vms_sysptbr = 0; /* system page table base */ | |
| t_uint64 vms_hwpcb = 0; /* hardware PCB */ | |
| t_uint64 vms_thread = 0; /* thread unique */ | |
| t_uint64 vms_prbr = 0; /* processor unique */ | |
| t_uint64 vms_stkp[4]; /* stack pointers */ | |
| t_uint64 vms_scbb = 0; /* SCB base */ | |
| t_uint64 vms_scc = 0; /* system cycle ctr */ | |
| t_uint64 vms_mces = 0; /* machine check err summ */ | |
| uint32 vms_ipl = 0; /* hardware IPL */ | |
| uint32 vms_cm = 0; /* inst current mode */ | |
| uint32 vms_sisr = 0; /* software int req */ | |
| uint32 vms_asten = 0; /* AST enables */ | |
| uint32 vms_astsr = 0; /* AST requests */ | |
| uint32 vms_last_pcc = 0; /* last pcc_l */ | |
| uint32 vms_datfx = 0; /* data alignment */ | |
| uint32 vms_ps = 0; /* static PS */ | |
| const uint32 ast_map[4] = { 0x1, 0x3, 0x7, 0xF }; | |
| const uint32 ast_pri[16] = { | |
| 0, MODE_K, MODE_E, MODE_K, MODE_S, MODE_K, MODE_E, MODE_K, | |
| MODE_U, MODE_K, MODE_E, MODE_K, MODE_S, MODE_K, MODE_E, MODE_K | |
| }; | |
| static const uint32 lnt_map[4] = { L_BYTE, L_WORD, L_LONG, L_QUAD }; | |
| static const int8 alg_map[64] = { | |
| ALG_ERR, ALG_ERR, ALG_ERR, ALG_ERR, | |
| ALG_ERR, ALG_ERR, ALG_ERR, ALG_ERR, | |
| ALG_ERR, ALG_ERR, ALG_ERR, ALG_ERR, | |
| ALG_W, ALG_W|ALG_ST, ALG_ERR, ALG_ERR, | |
| ALG_ERR, ALG_ERR, ALG_ERR, ALG_ERR, | |
| ALG_ERR, ALG_ERR, ALG_ERR, ALG_ERR, | |
| ALG_ERR, ALG_ERR, ALG_ERR, ALG_ERR, | |
| ALG_ERR, ALG_ERR, ALG_ERR, ALG_ERR, | |
| ALG_L, ALG_Q, ALG_L, ALG_Q, | |
| ALG_L|ALG_ST, ALG_Q|ALG_ST, ALG_L|ALG_ST, ALG_Q|ALG_ST, | |
| ALG_L, ALG_Q, ALG_INV, ALG_INV, | |
| ALG_L|ALG_ST, ALG_Q|ALG_ST, ALG_INV, ALG_INV, | |
| ALG_ERR, ALG_ERR, ALG_ERR, ALG_ERR, | |
| ALG_ERR, ALG_ERR, ALG_ERR, ALG_ERR, | |
| ALG_ERR, ALG_ERR, ALG_ERR, ALG_ERR, | |
| ALG_ERR, ALG_ERR, ALG_ERR, ALG_ERR | |
| }; | |
| extern t_uint64 R[32]; | |
| extern t_uint64 PC, trap_mask; | |
| extern t_uint64 p1; | |
| extern uint32 vax_flag, lock_flag; | |
| extern uint32 fpen; | |
| extern uint32 ir, pcc_h, pcc_l, pcc_enb; | |
| extern uint32 cm_racc, cm_wacc, cm_macc; | |
| extern uint32 mmu_ispage, mmu_dspage; | |
| extern jmp_buf save_env; | |
| extern uint32 int_req[IPL_HLVL]; | |
| t_int64 vms_insqhil (void); | |
| t_int64 vms_insqtil (void); | |
| t_int64 vms_insqhiq (void); | |
| t_int64 vms_insqtiq (void); | |
| t_int64 vms_insquel (uint32 defer); | |
| t_int64 vms_insqueq (uint32 defer); | |
| t_int64 vms_remqhil (void); | |
| t_int64 vms_remqtil (void); | |
| t_int64 vms_remqhiq (void); | |
| t_int64 vms_remqtiq (void); | |
| t_int64 vms_remquel (uint32 defer); | |
| t_int64 vms_remqueq (uint32 defer); | |
| t_int64 vms_insqhilr (void); | |
| t_int64 vms_insqtilr (void); | |
| t_int64 vms_insqhiqr (void); | |
| t_int64 vms_insqtiqr (void); | |
| t_int64 vms_remqhilr (void); | |
| t_int64 vms_remqtilr (void); | |
| t_int64 vms_remqhiqr (void); | |
| t_int64 vms_remqtiqr (void); | |
| uint32 vms_probe (uint32 acc); | |
| uint32 vms_amovrr (void); | |
| uint32 vms_amovrm (void); | |
| t_stat vms_rei (void); | |
| void vms_swpctx (void); | |
| t_stat vms_intexc (uint32 vec, uint32 newmode, uint32 newipl); | |
| t_stat vms_mm_intexc (uint32 vec, t_uint64 par2); | |
| t_stat pal_proc_reset_vms (DEVICE *dptr); | |
| t_uint64 ReadUna (t_uint64 va, uint32 lnt, uint32 acc); | |
| void WriteUna (t_uint64 va, t_uint64 val, uint32 lnt, uint32 acc); | |
| uint32 tlb_check (t_uint64 va); | |
| uint32 Test (t_uint64 va, uint32 acc, t_uint64 *pa); | |
| extern t_stat (*pal_eval_intr) (uint32 ipl); | |
| 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 (*pal_find_pte) (uint32 vpn, t_uint64 *pte); | |
| /* VMSPAL data structures | |
| vmspal_dev device descriptor | |
| vmspal_unit unit | |
| vmspal_reg register list | |
| */ | |
| UNIT vmspal_unit = { UDATA (NULL, 0, 0) }; | |
| REG vmspal_reg[] = { | |
| { HRDATA (KSP, ksp, 64) }, | |
| { HRDATA (ESP, esp, 64) }, | |
| { HRDATA (SSP, ssp, 64) }, | |
| { HRDATA (USP, usp, 64) }, | |
| { HRDATA (PTBR, vms_ptbr, 64) }, | |
| { HRDATA (VTBR, vms_vtbr, 64) }, | |
| { HRDATA (VIRBND, vms_virbnd, 64) }, | |
| { HRDATA (SYSPTBR, vms_sysptbr, 64) }, | |
| { HRDATA (THREAD, vms_thread, 64) }, | |
| { HRDATA (PRBR, vms_prbr, 64) }, | |
| { HRDATA (HWPCB, vms_hwpcb, 64) }, | |
| { HRDATA (SCBB, vms_scbb, 64) }, | |
| { HRDATA (SCC, vms_scc, 64) }, | |
| { HRDATA (LASTPCC, vms_last_pcc, 32), REG_HRO }, | |
| { HRDATA (MCES, vms_mces, 64) }, | |
| { HRDATA (PS, vms_ps, 13) }, | |
| { HRDATA (IPL, vms_ipl, 5) }, | |
| { HRDATA (CM, vms_cm, 2) }, | |
| { HRDATA (SISR, vms_sisr, 16) }, | |
| { HRDATA (ASTEN, vms_asten, 4) }, | |
| { HRDATA (ASTSR, vms_astsr, 4) }, | |
| { FLDATA (DATFX, vms_datfx, 0) }, | |
| { NULL } | |
| }; | |
| DEVICE vmspal_dev = { | |
| "VMSPAL", &vmspal_unit, vmspal_reg, NULL, | |
| 1, 16, 1, 1, 16, 8, | |
| NULL, NULL, &pal_proc_reset_vms, | |
| NULL, NULL, NULL, | |
| NULL, 0 | |
| }; | |
| /* VMS interrupt evaluator - returns IPL of highest priority interrupt */ | |
| uint32 pal_eval_intr_vms (uint32 lvl) | |
| { | |
| uint32 i; | |
| static const int32 sw_int_mask[32] = { | |
| 0xFFFE, 0xFFFC, 0xFFF8, 0xFFF0, /* 0 - 3 */ | |
| 0xFFE0, 0xFFC0, 0xFF80, 0xFF00, /* 4 - 7 */ | |
| 0xFE00, 0xFC00, 0xF800, 0xF000, /* 8 - B */ | |
| 0xE000, 0xC000, 0x8000, 0x0000, /* C - F */ | |
| 0x0000, 0x0000, 0x0000, 0x0000, /* 10+ */ | |
| 0x0000, 0x0000, 0x0000, 0x0000, | |
| 0x0000, 0x0000, 0x0000, 0x0000, | |
| 0x0000, 0x0000, 0x0000, 0x0000 | |
| }; | |
| vms_scc = vms_scc + ((pcc_l - vms_last_pcc) & M32); /* update scc */ | |
| vms_last_pcc = pcc_l; | |
| for (i = IPL_HMAX; i >= IPL_HMIN; i--) { /* chk hwre int */ | |
| if (i <= lvl) return 0; /* at ipl? no int */ | |
| if (int_req[i - IPL_HMIN]) return i; /* req != 0? int */ | |
| } | |
| if (vms_sisr & sw_int_mask[lvl]) { /* swre interrupt? */ | |
| for (i = IPL_SMAX; i > lvl; i--) { /* check swre int */ | |
| if ((vms_sisr >> i) & 1) /* req != 0? int */ | |
| return (AST_TST (i)? IPL_AST: i); /* check for AST */ | |
| } | |
| } | |
| return (AST_TST (lvl)? IPL_AST: 0); /* no swre, check AST */ | |
| } | |
| /* VMS interrupt dispatch - reached from top of execute loop */ | |
| t_stat pal_proc_intr_vms (uint32 lvl) | |
| { | |
| uint32 vec; | |
| t_stat r; | |
| if (lvl > IPL_HMAX) return SCPE_IERR; /* above max? */ | |
| else if (lvl >= IPL_HMIN) vec = io_get_vec (lvl); /* hwre? get vector */ | |
| else if (lvl > IPL_SMAX) return SCPE_IERR; /* above swre max? */ | |
| else if (lvl > 0) { /* swre int? */ | |
| if ((lvl == IPL_AST) && (vms_asten & vms_astsr & ast_map[vms_cm])) { | |
| uint32 astm = ast_pri[vms_astsr & 0xF]; /* get AST priority */ | |
| vms_astsr = vms_astsr & ~(1u << astm); /* clear hi pri */ | |
| vec = SCB_KAST + (astm << 4); | |
| } | |
| else { /* swre int */ | |
| vms_sisr = vms_sisr & ~(1u << lvl); | |
| vec = SCB_SISR0 + (lvl << 4); | |
| } | |
| } | |
| else return SCPE_IERR; /* bug */ | |
| if (vec == 0) vec = SCB_PASVR; /* passive release? */ | |
| r = vms_intexc (vec, MODE_K, lvl); /* do interrupt */ | |
| vms_ps = vms_ps | PSV_IP; /* set int in prog */ | |
| return r; | |
| } | |
| /* VMS trap dispatch - reached synchronously from bottom of execute loop */ | |
| t_stat pal_proc_trap_vms (uint32 tsum) | |
| { | |
| t_stat r; | |
| r = vms_intexc (SCB_ARITH, MODE_K, vms_ipl); /* arithmetic trap */ | |
| R[4] = trap_mask; /* set parameters */ | |
| R[5] = tsum; | |
| return r; | |
| } | |
| /* VMS exception dispatch - reached from the ABORT handler */ | |
| t_stat pal_proc_excp_vms (uint32 abval) | |
| { | |
| uint32 op, ra, lntc; | |
| int8 fl; | |
| t_stat r; | |
| switch (abval) { | |
| case EXC_RSVI: /* reserved instr */ | |
| return vms_intexc (SCB_RSVI, MODE_K, vms_ipl); /* trap */ | |
| case EXC_RSVO: /* reserved operand */ | |
| return vms_intexc (SCB_RSVO, MODE_K, vms_ipl); /* trap */ | |
| case EXC_ALIGN: /* unaligned */ | |
| op = I_GETOP (ir); /* get opcode */ | |
| ra = I_GETRA (ir); /* get RA */ | |
| fl = alg_map[op]; /* get alignment map */ | |
| if (fl == ALG_ERR) return SCPE_IERR; /* impossible? */ | |
| if (fl == ALG_INV) return (SCB_RSVI, MODE_K, vms_ipl); /* conditional? */ | |
| lntc = ALG_GETLNT (fl); /* get length code */ | |
| if (fl & ALG_ST) /* store? */ | |
| WriteUna (p1, R[ra], lnt_map[lntc], cm_wacc); | |
| else if (ra != 31) | |
| R[ra] = ReadUna (p1, lnt_map[lntc], cm_racc); | |
| if (vms_datfx) break; /* trap? */ | |
| r = vms_intexc (SCB_ALIGN, MODE_K, vms_ipl); /* do trap */ | |
| R[4] = p1; /* R4 = va */ | |
| R[5] = (fl & ALG_ST)? 1: 0; /* R5 = load/store */ | |
| return r; | |
| case EXC_FPDIS: /* fp disabled */ | |
| PC = (PC - 4) & M64; /* back up PC */ | |
| return vms_intexc (SCB_FDIS, MODE_K, vms_ipl); /* fault */ | |
| case EXC_FOX+EXC_E: /* FOE */ | |
| tlb_is (p1, TLB_CI); | |
| return vms_mm_intexc (SCB_FOE, VMS_MME_E); | |
| case EXC_FOX+EXC_R: /* FOR */ | |
| PC = (PC - 4) & M64; /* back up PC */ | |
| return vms_mm_intexc (SCB_FOR, VMS_MME_R); | |
| case EXC_FOX+EXC_W: /* FOW */ | |
| PC = (PC - 4) & M64; /* back up PC */ | |
| return vms_mm_intexc (SCB_FOW, VMS_MME_W); | |
| case EXC_BVA+EXC_E: | |
| case EXC_ACV+EXC_E: /* instr ACV */ | |
| return vms_mm_intexc (SCB_ACV, VMS_MME_E); | |
| case EXC_BVA+EXC_R: | |
| case EXC_ACV+EXC_R: /* data read ACV */ | |
| PC = (PC - 4) & M64; /* back up PC */ | |
| return vms_mm_intexc (SCB_ACV, VMS_MME_R); | |
| case EXC_BVA+EXC_W: | |
| case EXC_ACV+EXC_W: /* data write ACV */ | |
| PC = (PC - 4) & M64; /* back up PC */ | |
| return vms_mm_intexc (SCB_ACV, VMS_MME_W); | |
| case EXC_TNV+EXC_E: /* instr TNV */ | |
| tlb_is (p1, TLB_CI); | |
| return vms_mm_intexc (SCB_TNV, VMS_MME_E); | |
| case EXC_TNV+EXC_R: /* data read TNV */ | |
| tlb_is (p1, TLB_CD); | |
| PC = (PC - 4) & M64; /* back up PC */ | |
| return vms_mm_intexc (SCB_TNV, VMS_MME_R); | |
| case EXC_TNV+EXC_W: /* data write TNV */ | |
| tlb_is (p1, TLB_CD); | |
| PC = (PC - 4) & M64; /* back up PC */ | |
| return vms_mm_intexc (SCB_TNV, VMS_MME_W); | |
| case EXC_TBM + EXC_E: /* TLB miss */ | |
| case EXC_TBM + EXC_R: | |
| case EXC_TBM + EXC_W: | |
| return SCPE_IERR; /* should not occur */ | |
| default: | |
| return STOP_INVABO; | |
| } | |
| return SCPE_OK; | |
| } | |
| /* PALcode instruction dispatcher - function code verified in CPU */ | |
| t_stat pal_proc_inst_vms (uint32 fnc) | |
| { | |
| t_uint64 val; | |
| uint32 arg32 = (uint32) R[16]; | |
| if ((fnc < 0x40) && (vms_cm != MODE_K)) ABORT (EXC_RSVI); | |
| switch (fnc) { | |
| case OP_HALT: | |
| return STOP_HALT; | |
| case OP_CFLUSH: | |
| case OP_DRAINA: | |
| break; | |
| case OP_LDQP: | |
| R[0] = ReadPQ (R[16]); | |
| break; | |
| case OP_STQP: | |
| WritePQ (R[16], R[17]); | |
| break; | |
| case OP_SWPCTX: | |
| vms_swpctx (); | |
| break; | |
| case MF_ASN: | |
| R[0] = itlb_read_asn (); | |
| break; | |
| case MT_ASTEN: | |
| R[0] = vms_asten & AST_MASK; | |
| vms_asten = ((vms_asten & arg32) | (arg32 >> 4)) & AST_MASK; | |
| break; | |
| case MT_ASTSR: | |
| R[0] = vms_astsr & AST_MASK; | |
| vms_astsr = ((vms_astsr & arg32) | (arg32 >> 4)) & AST_MASK; | |
| break; | |
| case OP_CSERVE: | |
| // tbd | |
| break; | |
| case OP_SWPPAL: | |
| R[0] = 0; | |
| break; | |
| case MF_FEN: | |
| R[0] = fpen & 1; | |
| break; | |
| case MT_FEN: | |
| fpen = arg32 & 1; | |
| arg32 = ReadPL (vms_hwpcb + PCBV_FLAGS); | |
| arg32 = (arg32 & ~1) | fpen; | |
| WritePL (vms_hwpcb + PCBV_FLAGS, arg32); | |
| break; | |
| case MT_IPIR: | |
| //tbd | |
| break; | |
| case MF_IPL: | |
| R[0] = vms_ipl & PSV_M_IPL; | |
| break; | |
| case MT_IPL: | |
| R[0] = vms_ipl & PSV_M_IPL; | |
| vms_ipl = arg32 & PSV_M_IPL; | |
| break; | |
| case MF_MCES: | |
| R[0] = vms_mces; | |
| break; | |
| case MT_MCES: | |
| vms_mces = (vms_mces | (arg32 & MCES_DIS)) & ~(arg32 & MCES_W1C); | |
| break; | |
| case MF_PCBB: | |
| R[0] = vms_hwpcb; | |
| break; | |
| case MF_PRBR: | |
| R[0] = vms_prbr; | |
| break; | |
| case MT_PRBR: | |
| vms_prbr = R[16]; | |
| break; | |
| case MF_PTBR: | |
| R[0] = (vms_ptbr >> VA_N_OFF); /* PFN only */ | |
| break; | |
| case MF_SCBB: | |
| R[0] = vms_scbb; | |
| break; | |
| case MT_SCBB: | |
| vms_scbb = R[16]; | |
| break; | |
| case MF_SISR: | |
| R[0] = vms_sisr & SISR_MASK; | |
| break; | |
| case MT_SIRR: | |
| vms_sisr = (vms_sisr | (1u << (arg32 & 0xF))) & SISR_MASK; | |
| break; | |
| case MF_TBCHK: | |
| if (tlb_check (R[16])) R[0] = Q_SIGN + 1; | |
| else R[0] = Q_SIGN; | |
| break; | |
| case MT_TBIA: | |
| tlb_ia (TLB_CI | TLB_CD | TLB_CA); | |
| break; | |
| case MT_TBIAP: | |
| tlb_ia (TLB_CI | TLB_CD); | |
| break; | |
| case MT_TBIS: | |
| tlb_is (R[16], TLB_CI | TLB_CD | TLB_CA); | |
| break; | |
| case MF_ESP: | |
| R[0] = esp; | |
| break; | |
| case MT_ESP: | |
| esp = R[16]; | |
| break; | |
| case MF_SSP: | |
| R[0] = ssp; | |
| break; | |
| case MT_SSP: | |
| ssp = R[16]; | |
| break; | |
| case MF_USP: | |
| R[0] = usp; | |
| break; | |
| case MT_USP: | |
| usp = R[16]; | |
| break; | |
| case MT_TBISI: | |
| tlb_is (R[16], TLB_CI | TLB_CA); | |
| break; | |
| case MT_TBISD: | |
| tlb_is (R[16], TLB_CD | TLB_CA); | |
| break; | |
| case MF_ASTEN: | |
| R[0] = vms_asten & AST_MASK; | |
| break; | |
| case MF_ASTSR: | |
| R[0] = vms_astsr & AST_MASK; | |
| break; | |
| case MF_VTBR: | |
| R[0] = vms_vtbr; | |
| break; | |
| case MT_VTBR: | |
| vms_vtbr = R[16]; | |
| break; | |
| case MT_PERFMON: | |
| // tbd | |
| break; | |
| case MT_DATFX: | |
| vms_datfx = arg32 & 1; | |
| val = ReadPQ (vms_hwpcb + PCBV_FLAGS); | |
| val = (val & ~0x8000000000000000) | (((t_uint64) vms_datfx) << 63); | |
| WritePQ (vms_hwpcb + PCBV_FLAGS, val); | |
| break; | |
| case MF_VIRBND: | |
| R[0] = vms_virbnd; | |
| break; | |
| case MT_VIRBND: | |
| vms_virbnd = R[16]; | |
| break; | |
| case MF_SYSPTBR: | |
| R[0] = vms_sysptbr; | |
| break; | |
| case MT_SYSPTBR: | |
| vms_sysptbr = R[16]; | |
| break; | |
| case OP_WTINT: | |
| R[0] = 0; | |
| break; | |
| case MF_WHAMI: | |
| R[0] = 0; | |
| break; | |
| /* Non-privileged */ | |
| case OP_BPT: | |
| return vms_intexc (SCB_BPT, MODE_K, vms_ipl); | |
| case OP_BUGCHK: | |
| return vms_intexc (SCB_BUG, MODE_K, vms_ipl); | |
| case OP_CHME: | |
| return vms_intexc (SCB_CHME, MOST_PRIV (MODE_E, vms_cm), vms_ipl); | |
| case OP_CHMK: | |
| return vms_intexc (SCB_CHMK, MODE_K, vms_ipl); | |
| case OP_CHMS: | |
| return vms_intexc (SCB_CHMS, MOST_PRIV (MODE_S, vms_cm), vms_ipl); | |
| case OP_CHMU: | |
| return vms_intexc (SCB_CHMU, vms_cm, vms_ipl); | |
| break; | |
| case OP_IMB: | |
| break; | |
| case OP_INSQHIL: | |
| R[0] = vms_insqhil (); | |
| break; | |
| case OP_INSQTIL: | |
| R[0] = vms_insqtil (); | |
| break; | |
| case OP_INSQHIQ: | |
| R[0] = vms_insqhiq (); | |
| break; | |
| case OP_INSQTIQ: | |
| R[0] = vms_insqtiq (); | |
| break; | |
| case OP_INSQUEL: | |
| R[0] = vms_insquel (0); | |
| break; | |
| case OP_INSQUEQ: | |
| R[0] = vms_insqueq (0); | |
| break; | |
| case OP_INSQUELD: | |
| R[0] = vms_insquel (1); | |
| break; | |
| case OP_INSQUEQD: | |
| R[0] = vms_insqueq (1); | |
| break; | |
| case OP_PROBER: | |
| R[0] = vms_probe (PTE_KRE); | |
| break; | |
| case OP_PROBEW: | |
| R[0] = vms_probe (PTE_KRE|PTE_KWE); | |
| break; | |
| case OP_RD_PS: | |
| R[0] = GET_PSV; | |
| break; | |
| case OP_REI: | |
| return vms_rei (); | |
| case OP_REMQHIL: | |
| R[0] = vms_insqhil (); | |
| break; | |
| case OP_REMQTIL: | |
| R[0] = vms_remqtil (); | |
| break; | |
| case OP_REMQHIQ: | |
| R[0] = vms_remqhiq (); | |
| break; | |
| case OP_REMQTIQ: | |
| R[0] = vms_remqtiq (); | |
| break; | |
| case OP_REMQUEL: | |
| R[0] = vms_remquel (0); | |
| break; | |
| case OP_REMQUEQ: | |
| R[0] = vms_remqueq (0); | |
| break; | |
| case OP_REMQUELD: | |
| R[0] = vms_remquel (1); | |
| break; | |
| case OP_REMQUEQD: | |
| R[0] = vms_remqueq (1); | |
| break; | |
| case OP_SWASTEN: | |
| R[0] = (vms_asten >> vms_cm) & 1; | |
| vms_asten = (vms_asten & ~(1u << vms_cm)) | ((arg32 & 1) << vms_cm); | |
| break; | |
| case OP_WR_PS_SW: | |
| vms_ps = (vms_ps & ~PSV_M_SW) | (arg32 & PSV_M_SW); | |
| break; | |
| case OP_RSCC: | |
| vms_scc = vms_scc + ((pcc_l - vms_last_pcc) & M32); /* update scc */ | |
| vms_last_pcc = pcc_l; | |
| R[0] = vms_scc; | |
| break; | |
| case OP_RD_UNQ: | |
| R[0] = vms_thread; | |
| break; | |
| case OP_WR_UNQ: | |
| vms_thread = R[16]; | |
| break; | |
| case OP_AMOVRR: | |
| R[18] = vms_amovrr (); | |
| break; | |
| case OP_AMOVRM: | |
| R[18] = vms_amovrm (); | |
| break; | |
| case OP_INSQHILR: | |
| R[0] = vms_insqhilr (); | |
| break; | |
| case OP_INSQTILR: | |
| R[0] = vms_insqtilr (); | |
| break; | |
| case OP_INSQHIQR: | |
| R[0] = vms_insqhiqr (); | |
| break; | |
| case OP_INSQTIQR: | |
| R[0] = vms_insqtiqr (); | |
| break; | |
| case OP_REMQHILR: | |
| R[0] = vms_insqhilr (); | |
| break; | |
| case OP_REMQTILR: | |
| R[0] = vms_remqtilr (); | |
| break; | |
| case OP_REMQHIQR: | |
| R[0] = vms_remqhiqr (); | |
| break; | |
| case OP_REMQTIQR: | |
| R[0] = vms_remqtiqr (); | |
| break; | |
| case OP_GENTRAP: | |
| return vms_intexc (SCB_GENTRAP, MODE_K, vms_ipl); | |
| case OP_CLRFEN: | |
| fpen = 0; | |
| arg32 = ReadPL (vms_hwpcb + PCBV_FLAGS); | |
| arg32 = arg32 & ~1; | |
| WritePL (vms_hwpcb + PCBV_FLAGS, arg32); | |
| break; | |
| default: | |
| ABORT (EXC_RSVI); | |
| } | |
| return SCPE_OK; | |
| } | |
| /* Interlocked insert instructions | |
| R[16] = entry | |
| R[17] = header | |
| Pictorially: | |
| BEFORE AFTER INSQHI AFTER INSQTI | |
| H: A-H H: D-H W H: A-H W for interlock | |
| H+4/8: C-H H+4/8: C-H H+4/8: D-H W | |
| A: B-A A: B-A A: B-A | |
| A+4/8: H-A A+4/8: D-A W A+4/8: H-A | |
| B: C-B B: C-B B: C-B | |
| B+4/8: A-B B+4/8: A-B B+4/8: A-B | |
| C: H-C C: H-C C: D-C W | |
| C+4/8: B-C C+4/8: B-C C+4/8: B-C | |
| D: --- D: A-D W D: H-D W | |
| D+4/8: --- D+4/8: H-D W D+4/8: C-D W | |
| Note that the queue header, the entry to be inserted, and all | |
| the intermediate entries that are "touched" in any way must be | |
| QUAD(OCTA)WORD aligned. In addition, the header and the entry | |
| must not be equal. | |
| Note that the offset arithmetic (+4, +8) cannot overflow 64b, | |
| because the entries are quad or octa aligned. | |
| */ | |
| t_int64 vms_insqhil (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 d = R[17]; | |
| t_uint64 ar, a; | |
| if ((h == d) || ((h | d) & 07) || /* h, d quad align? */ | |
| ((SEXT_L_Q (h) & M64) != h) || | |
| ((SEXT_L_Q (d) & M64) != d)) ABORT (EXC_RSVO); | |
| ReadAccQ (d, cm_wacc); /* wchk (d) */ | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if (ar & 06) ABORT (EXC_RSVO); /* a quad align? */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| WriteQ (h, ar | 1); /* get interlock */ | |
| a = (SEXT_L_Q (ar + h)) & M64; /* abs addr of a */ | |
| if (Test (a, cm_wacc, NULL)) WriteQ (h, ar); /* wtst a, rls if err */ | |
| WriteL (a + 4, (uint32) (d - a)); /* (a+4) <- d-a, flt ok */ | |
| WriteL (d, (uint32) (a - d)); /* (d) <- a-d */ | |
| WriteL (d + 4, (uint32) (h - d)); /* (d+4) <- h-d */ | |
| WriteL (h, (uint32) (d - h)); /* (h) <- d-h, rls int */ | |
| return ((ar & M32) == 0)? 0: +1; /* ret 0 if q was empty */ | |
| } | |
| t_int64 vms_insqhilr (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 d = R[17]; | |
| t_uint64 ar, a; | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| WriteQ (h, ar | 1); /* get interlock */ | |
| a = (SEXT_L_Q (ar + h)) & M64; /* abs addr of a */ | |
| WriteL (a + 4, (uint32) (d - a)); /* (a+4) <- d-a, flt ok */ | |
| WriteL (d, (uint32) (a - d)); /* (d) <- a-d */ | |
| WriteL (d + 4, (uint32) (h - d)); /* (d+4) <- h-d */ | |
| WriteL (h, (uint32) (d - h)); /* (h) <- d-h, rls int */ | |
| return ((ar & M32) == 0)? 0: +1; /* ret 0 if q was empty */ | |
| } | |
| t_int64 vms_insqhiq (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 d = R[17]; | |
| t_uint64 ar, a; | |
| if ((h == d) || ((h | d) & 0xF)) ABORT (EXC_RSVO); /* h, d octa align? */ | |
| ReadAccQ (d, cm_wacc); /* wchk (d) */ | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if (ar & 0xE) ABORT (EXC_RSVO); /* a octa align? */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| WriteQ (h, ar | 1); /* get interlock */ | |
| a = (ar + h) & M64; /* abs addr of a */ | |
| if (Test (a, cm_wacc, NULL)) WriteQ (h, ar); /* wtst a, rls if err */ | |
| WriteQ (a + 8, (d - a) & M64); /* (a+8) <- d-a, flt ok */ | |
| WriteQ (d, (a - d) & M64); /* (d) <- a-d */ | |
| WriteQ (d + 8, (h - d) & M64); /* (d+8) <- h-d */ | |
| WriteQ (h, (d - h) & M64); /* (h) <- d-h, rls int */ | |
| return (ar == 0)? 0: +1; /* ret 0 if q was empty */ | |
| } | |
| t_int64 vms_insqhiqr (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 d = R[17]; | |
| t_uint64 ar, a; | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| WriteQ (h, ar | 1); /* get interlock */ | |
| a = (ar + h) & M64; /* abs addr of a */ | |
| WriteQ (a + 8, (d - a) & M64); /* (a+8) <- d-a, flt ok */ | |
| WriteQ (d, (a - d) & M64); /* (d) <- a-d */ | |
| WriteQ (d + 8, (h - d) & M64); /* (d+8) <- h-d */ | |
| WriteQ (h, (d - h) & M64); /* (h) <- d-h, rls int */ | |
| return (ar == 0)? 0: +1; /* ret 0 if q was empty */ | |
| } | |
| t_int64 vms_insqtil (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 d = R[17]; | |
| t_uint64 ar, c; | |
| if ((h == d) || ((h | d) & 07) || /* h, d quad align? */ | |
| ((SEXT_L_Q (h) & M64) != h) || | |
| ((SEXT_L_Q (d) & M64) != d)) ABORT (EXC_RSVO); | |
| ReadAccQ (d, cm_wacc); /* wchk (d) */ | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if ((ar & M32) == 0) return vms_insqhil (); /* if empty, ins hd */ | |
| if (ar & 06) ABORT (EXC_RSVO); /* a quad align? */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| c = ar >> 32; /* c <- (h+4) */ | |
| c = (SEXT_L_Q (c + h)) & M64; /* abs addr of c */ | |
| if (c & 07) { /* c quad aligned? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| ABORT (EXC_RSVO); /* fault */ | |
| } | |
| if (Test (c, cm_wacc, NULL)) WriteQ (h, ar); /* wtst c, rls if err */ | |
| WriteL (c, (uint32) (d - c)); /* (c) <- d-c, flt ok */ | |
| WriteL (d, (uint32) (h - d)); /* (d) <- h-d */ | |
| WriteL (d + 4, (uint32) (c - d)); /* (d+4) <- c-d */ | |
| WriteL (h + 4, (uint32) (d - h)); /* (h+4) <- d-h */ | |
| WriteL (h, (uint32) ar); /* release interlock */ | |
| return 0; /* q was not empty */ | |
| } | |
| t_int64 vms_insqtilr (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 d = R[17]; | |
| t_uint64 ar, c; | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if ((ar & M32) == 0) return vms_insqhilr (); /* if empty, ins hd */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| c = ar >> 32; /* c <- (h+4) */ | |
| c = (SEXT_L_Q (c + h)) & M64; /* abs addr of c */ | |
| WriteL (c, (uint32) (d - c)); /* (c) <- d-c */ | |
| WriteL (d, (uint32) (h - d)); /* (d) <- h-d */ | |
| WriteL (d + 4, (uint32) (c - d)); /* (d+4) <- c-d */ | |
| WriteL (h + 4, (uint32) (d - h)); /* (h+4) <- d-h */ | |
| WriteL (h, (uint32) ar); /* release interlock */ | |
| return 0; /* q was not empty */ | |
| } | |
| t_int64 vms_insqtiq (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 d = R[17]; | |
| t_uint64 ar, c; | |
| if ((h == d) || ((h | d) & 0xF)) ABORT (EXC_RSVO); /* h, d octa align? */ | |
| ReadAccQ (d, cm_wacc); /* wchk ent */ | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if (ar == 0) return vms_insqhiq (); /* if empty, ins hd */ | |
| if (ar & 0xE) ABORT (EXC_RSVO); /* a octa align? */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| c = ReadQ (h + 8); /* c <- (h+8) */ | |
| c = (c + h) & M64; /* abs addr of C */ | |
| if (c & 0xF) { /* c octa aligned? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| ABORT (EXC_RSVO); /* fault */ | |
| } | |
| if (Test (c, cm_wacc, NULL)) WriteQ (h, ar); /* wtst c, rls if err */ | |
| WriteQ (c, (d - c) & M64); /* (c) <- d-c, flt ok */ | |
| WriteQ (d, (h - d) & M64); /* (d) <- h-d */ | |
| WriteQ (d + 8, (c - d) & M64); /* (d+8) <- c-d */ | |
| WriteQ (h + 8, (d - h) & M64); /* (h+8) <- d-h */ | |
| WriteQ (h, ar); /* release interlock */ | |
| return 0; /* q was not empty */ | |
| } | |
| t_int64 vms_insqtiqr (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 d = R[17]; | |
| t_uint64 ar, c; | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if (ar == 0) return vms_insqhiqr (); /* if empty, ins hd */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| c = ReadQ (h + 8); /* c <- (h+8) */ | |
| c = (c + h) & M64; /* abs addr of C */ | |
| WriteQ (c, (d - c) & M64); /* (c) <- d-c */ | |
| WriteQ (d, (h - d) & M64); /* (d) <- h-d */ | |
| WriteQ (d + 8, (c - d) & M64); /* (d+8) <- c-d */ | |
| WriteQ (h + 8, (d - h) & M64); /* (h+8) <- d-h */ | |
| WriteQ (h, ar); /* release interlock */ | |
| return 0; /* q was not empty */ | |
| } | |
| /* Interlocked remove instructions | |
| R[16] = header (hdr.aq) | |
| R[1] ] = receives destination address | |
| Pictorially: | |
| BEFORE AFTER REMQHI AFTER REMQTI | |
| H: A-H H: B-H W H: A-H W for interlock | |
| H+4/8: C-H H+4/8: C-H H+4/8: B-H W | |
| A: B-A A: B-A R A: B-A | |
| A+4/8: H-A A+4/8: H-A A+4/8: H-A | |
| B: C-B B: C-B B: H-B W | |
| B+4/8: A-B B+4/8: H-B W B+4/8: A-B | |
| C: H-C C: H-C C: H-C | |
| C+4/8: B-C C+4/8: B-C C+4/8: B-C R | |
| Note that the queue header and all the entries that are | |
| "touched" in any way must be QUAD(OCTA)WORD aligned. | |
| */ | |
| t_int64 vms_remqhil (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 ar, a, b; | |
| if ((h & 07) || ((SEXT_L_Q (h) & M64) != h)) /* h quad aligned? */ | |
| ABORT (EXC_RSVO); | |
| ar = ReadQ (h); /* ar <- (h) */ | |
| if (ar & 06) ABORT (EXC_RSVO); /* a quad aligned? */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| if ((ar & M32) == 0) return 0; /* queue empty? */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| a = (SEXT_L_Q (ar + h)) & M64; /* abs addr of a */ | |
| if (Test (a, cm_racc, NULL)) WriteQ (h, ar); /* rtst a, rls if err */ | |
| b = ReadL (a); /* b <- (a), flt ok */ | |
| b = (SEXT_L_Q (b + a)) & M64; /* abs addr of b */ | |
| if (b & 07) { /* b quad aligned? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| ABORT (EXC_RSVO); /* fault */ | |
| } | |
| if (Test (b, cm_wacc, NULL)) WriteQ (h, ar); /* wtst b, rls if err */ | |
| WriteL (b + 4, (uint32) (h - b)); /* (b+4) <- h-b, flt ok */ | |
| WriteL (h, (uint32) (b - h)); /* (h) <- b-h, rls int */ | |
| R[1] = a; /* address of entry */ | |
| return ((b & M32) == (h & M32))? +2: +1; /* if b = h, q empty */ | |
| } | |
| t_int64 vms_remqhilr (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 ar, a, b; | |
| ar = ReadQ (h); /* ar <- (h) */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| if ((ar & M32) == 0) return 0; /* queue empty? */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| a = (SEXT_L_Q (ar + h)) & M64; /* abs addr of a */ | |
| b = ReadL (a); /* b <- (a), flt ok */ | |
| b = (SEXT_L_Q (b + a)) & M64; /* abs addr of b */ | |
| WriteL (b + 4, (uint32) (h - b)); /* (b+4) <- h-b, flt ok */ | |
| WriteL (h, (uint32) (b - h)); /* (h) <- b-h, rls int */ | |
| R[1] = a; /* address of entry */ | |
| return ((b & M32) == (h & M32))? +2: +1; /* if b = h, q empty */ | |
| } | |
| t_int64 vms_remqhiq (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 ar, a, b; | |
| if (h & 0xF) ABORT (EXC_RSVO); /* h octa aligned? */ | |
| ar = ReadQ (h); /* ar <- (h) */ | |
| if (ar & 0xE) ABORT (EXC_RSVO); /* a octa aligned? */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| if (ar == 0) return 0; /* queue empty? */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| a = (ar + h) & M64; /* abs addr of a */ | |
| if (Test (a, cm_racc, NULL)) WriteQ (h, ar); /* rtst a, rls if err */ | |
| b = ReadQ (a); /* b <- (a), flt ok */ | |
| b = (b + a) & M64; /* abs addr of b */ | |
| if (b & 0xF) { /* b octa aligned? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| ABORT (EXC_RSVO); /* fault */ | |
| } | |
| if (Test (b, cm_wacc, NULL)) WriteQ (h, ar); /* wtst b, rls if err */ | |
| WriteQ (b + 8, (h - b) & M64); /* (b+8) <- h-b, flt ok */ | |
| WriteQ (h, (b - h) & M64); /* (h) <- b-h, rls int */ | |
| R[1] = a; /* address of entry */ | |
| return (b == h)? +2: +1; /* if b = h, q empty */ | |
| } | |
| t_int64 vms_remqhiqr (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 ar, a, b; | |
| ar = ReadQ (h); /* ar <- (h) */ | |
| if (ar & 01) return -1; /* busy, ret -1 */ | |
| if (ar == 0) return 0; /* queue empty? */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| a = (ar + h) & M64; /* abs addr of a */ | |
| b = ReadQ (a); /* b <- (a) */ | |
| b = (b + a) & M64; /* abs addr of b */ | |
| WriteQ (b + 8, (h - b) & M64); /* (b+8) <- h-b, flt ok */ | |
| WriteQ (h, (b - h) & M64); /* (h) <- b-h, rls int */ | |
| R[1] = a; /* address of entry */ | |
| return (b == h)? +2: +1; /* if b = h, q empty */ | |
| } | |
| t_int64 vms_remqtil (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 ar, b, c; | |
| if ((h & 07) || ((SEXT_L_Q (h) & M64) != h)) /* h quad aligned? */ | |
| ABORT (EXC_RSVO); | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if (ar & 06) ABORT (EXC_RSVO); /* a quad aligned? */ | |
| if (ar & 01) return -1; /* busy, return - 1*/ | |
| if ((ar & M32) == 0) return 0; /* empty, return 0 */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| c = ar >> 32; /* c <- (h+4) */ | |
| if (c & 07) { /* c quad aligned? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| ABORT (EXC_RSVO); /* fault */ | |
| } | |
| if ((ar & M32) == (c & M32)) { /* single entry? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| return vms_remqhil (); /* treat as remqhil */ | |
| } | |
| c = (SEXT_L_Q (c + h)) & M64; /* abs addr of c */ | |
| if (Test (c + 4, cm_racc, NULL)) WriteQ (h, ar); /* rtst c+4, rls if err */ | |
| b = ReadL (c + 4); /* b <- (c+4), flt ok */ | |
| b = (SEXT_L_Q (b + c)) & M64; /* abs addr of b */ | |
| if (b & 07) { /* b quad aligned? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| ABORT (EXC_RSVO); /* fault */ | |
| } | |
| if (Test (b, cm_wacc, NULL)) WriteQ (h, ar); /* wtst b, rls if err */ | |
| WriteL (b, (uint32) (h - b)); /* (b) <- h-b, flt ok */ | |
| WriteL (h + 4, (uint32) (b - h)); /* (h+4) <- b-h */ | |
| WriteL (h, (uint32) ar); /* release interlock */ | |
| R[1] = c; /* store result */ | |
| return +1; /* q can't be empty */ | |
| } | |
| t_int64 vms_remqtilr (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 ar, b, c; | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if (ar & 01) return -1; /* busy, return - 1*/ | |
| if ((ar & M32) == 0) return 0; /* emtpy, return 0 */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| c = ar >> 32; /* c <- (h+4) */ | |
| if ((ar & M32) == (c & M32)) { /* single entry? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| return vms_remqhilr (); /* treat as remqhil */ | |
| } | |
| c = (SEXT_L_Q (c + h)) & M64; /* abs addr of c */ | |
| b = ReadL (c + 4); /* b <- (c+4) */ | |
| b = (SEXT_L_Q (b) + c) & M64; /* abs addr of b */ | |
| WriteL (b, (uint32) (h - b)); /* (b) <- h-b */ | |
| WriteL (h + 4, (uint32) (b - h)); /* (h+4) <- b-h */ | |
| WriteL (h, (uint32) ar); /* release interlock */ | |
| R[1] = c; /* store result */ | |
| return +1; /* q can't be empty */ | |
| } | |
| t_int64 vms_remqtiq (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 ar, b, c; | |
| if (h & 0xF) ABORT (EXC_RSVO); /* h octa aligned? */ | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if (ar & 0xE) ABORT (EXC_RSVO); /* a quad aligned? */ | |
| if (ar & 01) return -1; /* busy, return - 1*/ | |
| if (ar == 0) return 0; /* emtpy, return 0 */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| c = ReadQ (h + 8); /* c <- (h+8) */ | |
| if (c & 0xF) { /* c octa aligned? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| ABORT (EXC_RSVO); /* fault */ | |
| } | |
| if (ar == c) { /* single entry? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| return vms_remqhiq (); /* treat as remqhil */ | |
| } | |
| c = (c + h) & M64; /* abs addr of c */ | |
| if (Test (c + 8, cm_racc, NULL)) WriteQ (h, ar); /* rtst c+8, rls if err */ | |
| b = ReadQ (c + 8); /* b <- (c+8), flt ok */ | |
| b = (b + c) & M64; /* abs addr of b */ | |
| if (b & 0xF) { /* b octa aligned? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| ABORT (EXC_RSVO); /* fault */ | |
| } | |
| if (Test (b, cm_wacc, NULL)) WriteQ (h, ar); /* wtst b, rls if err */ | |
| WriteQ (b, (h - b) & M64); /* (b) <- h-b, flt ok */ | |
| WriteQ (h + 8, (b - h) & M64); /* (h+8) <- b-h */ | |
| WriteQ (h, ar); /* release interlock */ | |
| R[1] = c; /* store result */ | |
| return +1; /* q can't be empty */ | |
| } | |
| t_int64 vms_remqtiqr (void) | |
| { | |
| t_uint64 h = R[16]; | |
| t_uint64 ar, b, c; | |
| ar = ReadQ (h); /* a <- (h) */ | |
| if (ar & 01) return -1; /* busy, return - 1*/ | |
| if (ar == 0) return 0; /* emtpy, return 0 */ | |
| WriteQ (h, ar | 1); /* acquire interlock */ | |
| c = ReadQ (h + 8); /* c <- (h+8) */ | |
| if (ar == c) { /* single entry? */ | |
| WriteQ (h, ar); /* release interlock */ | |
| return vms_remqhiq (); /* treat as remqhil */ | |
| } | |
| c = (c + h) & M64; /* abs addr of c */ | |
| b = ReadQ (c + 8); /* b <- (c+8) */ | |
| b = (b + c) & M64; /* abs addr of b */ | |
| WriteQ (b, (h - b) & M64); /* (b) <- h-b */ | |
| WriteQ (h + 8, (b - h) & M64); /* (h+8) <- b-h */ | |
| WriteQ (h, ar); /* release interlock */ | |
| R[1] = c; /* store result */ | |
| return +1; /* q can't be empty */ | |
| } | |
| /* INSQUE | |
| R[16] = predecessor address | |
| R[17] = entry address | |
| All writes must be checked before any writes are done. | |
| Pictorially: | |
| BEFORE AFTER | |
| P: S P: E W | |
| P+4/8: (n/a) P+4/8: (n/a) | |
| E: --- E: S W | |
| E+4/8: --- E+4/8: P W | |
| S: (n/a) S: (n/a) | |
| S+4/8: P S+4/8: E W | |
| For longword queues, operands can be misaligned. | |
| Quadword queues must be octaword aligned, and the | |
| address addition cannot overflow 64b. | |
| Note that WriteUna masks data to its proper length. | |
| */ | |
| t_int64 vms_insquel (uint32 defer) | |
| { | |
| t_uint64 p = SEXT_L_Q (R[16]) & M64; | |
| t_uint64 e = SEXT_L_Q (R[17]) & M64; | |
| t_uint64 s; | |
| if (defer) { /* defer? */ | |
| p = ReadUna (p, L_LONG, cm_racc); /* get address */ | |
| p = SEXT_L_Q (p) & M64; /* make 64b */ | |
| } | |
| s = ReadUna (p, L_LONG, cm_macc); /* s <- (p), wchk */ | |
| s = SEXT_L_Q (s) & M64; /* make 64b */ | |
| ReadUna ((s + 4) & M64, L_LONG, cm_wacc); /* wchk s+4 */ | |
| ReadUna ((e + 4) & M64, L_LONG, cm_wacc); /* wchk e+4 */ | |
| WriteUna (e, s, L_LONG, cm_wacc); /* (e) <- s, last unchecked */ | |
| WriteUna ((e + 4) & M64, p, L_LONG, cm_wacc); /* (e+4) <- p */ | |
| WriteUna ((s + 4) & M64, e, L_LONG, cm_wacc); /* (s+4) <- ent */ | |
| WriteUna (p, e, L_LONG, cm_wacc); /* (p) <- e */ | |
| return (((s & M32) == (p & M32))? +1: 0); /* return status */ | |
| } | |
| t_int64 vms_insqueq (uint32 defer) | |
| { | |
| t_uint64 p = R[16]; | |
| t_uint64 e = R[17]; | |
| t_uint64 s; | |
| if (defer) { /* defer? */ | |
| if (p & 07) ABORT (EXC_RSVO); | |
| p = ReadQ (p); | |
| } | |
| if ((e | p) & 0xF) ABORT (EXC_RSVO); /* p, e octa aligned? */ | |
| s = ReadAccQ (p, cm_macc); /* s <- (p), wchk */ | |
| if (s & 0xF) ABORT (EXC_RSVO); /* s octa aligned? */ | |
| ReadAccQ (s + 8, cm_wacc); /* wchk s+8 */ | |
| ReadAccQ (e + 8, cm_wacc); /* wchk e+8 */ | |
| WriteQ (e, s); /* (e) <- s */ | |
| WriteQ (e + 8, p); /* (e+8) <- p */ | |
| WriteQ (s + 8, e); /* (s+8) <- ent */ | |
| WriteQ (p, e); /* (p) <- e */ | |
| return ((s == p)? +1: 0); /* return status */ | |
| } | |
| /* REMQUE | |
| R[16] = entry address | |
| All writes must be checked before any writes are done. | |
| Pictorially: | |
| BEFORE AFTER | |
| P: E P: S W | |
| P+4/8: (n/a) P+4/8: (n/a) | |
| E: S W E: S | |
| E+4/8: P W E+4/8: P | |
| S: (n/a) S: (n/a) | |
| S+4/8: E W S+4/8: P | |
| */ | |
| t_int64 vms_remquel (uint32 defer) | |
| { | |
| t_uint64 e = SEXT_L_Q (R[16]) & M64; | |
| t_uint64 s, p; | |
| if (defer) { /* defer? */ | |
| e = ReadUna (e, L_LONG, cm_racc); /* get address */ | |
| e = SEXT_L_Q (e) & M64; /* make 64b */ | |
| } | |
| s = ReadUna (e, L_LONG, cm_racc); /* s <- (e) */ | |
| p = ReadUna ((e + 4) & M64, L_LONG, cm_racc); /* p <- (e+4) */ | |
| s = SEXT_L_Q (s) & M64; | |
| p = SEXT_L_Q (p) & M64; | |
| if (e == p) return -1; /* queue empty? */ | |
| ReadUna ((s + 4) & M64, L_LONG, cm_wacc); /* wchk (s+4) */ | |
| WriteUna (p, s, L_LONG, cm_wacc); /* (p) <- s */ | |
| WriteUna ((s + 4) & M64, p, L_LONG, cm_wacc); /* (s+4) <- p */ | |
| return ((s == p)? 0: +1); | |
| } | |
| t_int64 vms_remqueq (uint32 defer) | |
| { | |
| t_uint64 e = R[16]; | |
| t_uint64 s, p; | |
| if (defer) { /* defer? */ | |
| if (e & 07) ABORT (EXC_RSVO); | |
| e = ReadQ (e); | |
| } | |
| if (e & 0xF) ABORT (EXC_RSVO); /* e octa aligned? */ | |
| s = ReadQ (e); /* s <- (e) */ | |
| p = ReadQ (e + 8); /* p <- (e+8) */ | |
| if ((s | p) & 0xF) ABORT (EXC_RSVO); /* s, p octa aligned? */ | |
| if (e == p) return -1; /* queue empty? */ | |
| ReadAccQ (s + 8, cm_wacc); /* wchk (s+8) */ | |
| WriteQ (p, s); /* (p) <- s */ | |
| WriteQ (s + 8, p); /* (s+8) <- p */ | |
| return ((s == p)? 0: +1); | |
| } | |
| /* Probe */ | |
| uint32 vms_probe (uint32 acc) | |
| { | |
| uint32 pm = ((uint32) R[18]) & 3; | |
| if (pm <= vms_cm) pm = vms_cm; /* least privileged */ | |
| acc = (acc << pm) | PTE_V; /* access test - no FOR/W */ | |
| if (Test (R[16], acc, NULL)) return 0; /* test start */ | |
| if (Test ((R[16] + R[17]) & M64, acc, NULL)) return 0; /* test end */ | |
| return 1; | |
| } | |
| /* VMS TIE support instructions */ | |
| uint32 vms_amovrr (void) | |
| { | |
| uint32 lnt1 = ((uint32) R[18]) & 3; | |
| uint32 lnt2 = ((uint32) R[21]) & 3; | |
| if (vax_flag == 0) return 0; /* stop if !vax_flag */ | |
| vax_flag = 0; /* clear vax_flag */ | |
| ReadUna (R[17], lnt_map[lnt1], cm_wacc); /* verify writes */ | |
| ReadUna (R[20], lnt_map[lnt2], cm_wacc); | |
| WriteUna (R[17], R[16], lnt_map[lnt1], cm_wacc); /* do both writes */ | |
| WriteUna (R[20], R[21], lnt_map[lnt2], cm_wacc); /* WriteUna masks data */ | |
| return 1; | |
| } | |
| uint32 vms_amovrm (void) | |
| { | |
| t_uint64 va, va1; | |
| uint32 lnt1 = ((uint32) R[18]) & 3; | |
| uint32 lnt2 = ((uint32) R[21]) & 0x3F; | |
| uint32 i, dat; | |
| if (vax_flag == 0) return 0; /* stop if !vax_flag */ | |
| vax_flag = 0; /* clear vax_flag */ | |
| if (lnt2 && ((R[19] | R[20]) & 3)) ABORT (EXC_RSVO); /* lw aligned? */ | |
| ReadUna (R[17], lnt_map[lnt1], cm_wacc); /* verify first write */ | |
| if (lnt2) { /* if second length */ | |
| va = (R[19] + (lnt2 << 2) - 4) & M64; | |
| va1 = (R[20] + (lnt2 << 2) - 4) & M64; | |
| ReadL (R[19]); /* verify source */ | |
| ReadL (va); | |
| ReadAccL (R[20], cm_wacc); /* verify destination */ | |
| ReadAccL (va1, cm_wacc); | |
| } | |
| WriteUna (R[17], R[16], lnt_map[lnt1], cm_wacc); /* do first write */ | |
| for (i = 0, va = R[19], va1 = R[20]; i < lnt2; i++) { /* move data */ | |
| dat = ReadL (va); | |
| WriteL (va1, dat); | |
| va = (va + 4) & M64; | |
| va1 = (va1 + 4) & M64; | |
| } | |
| return 1; | |
| } | |
| /* Swap privileged context */ | |
| void vms_swpctx (void) | |
| { | |
| t_uint64 val; | |
| uint32 tmp; | |
| if (R[16] & 0x7F) ABORT (EXC_RSVO); /* must be 128B aligned */ | |
| WritePQ (vms_hwpcb + 0, SP); /* save stack ptrs */ | |
| WritePQ (vms_hwpcb + 8, esp); | |
| WritePQ (vms_hwpcb + 16, ssp); | |
| WritePQ (vms_hwpcb + 24, usp); | |
| WritePQ (vms_hwpcb + 48, (vms_astsr << 4) | vms_asten); /* save AST */ | |
| WritePQ (vms_hwpcb + 64, (pcc_h + pcc_l) & M32); /* save PCC */ | |
| WritePQ (vms_hwpcb + 72, vms_thread); /* save UNIQUE */ | |
| vms_hwpcb = R[16]; /* new PCB */ | |
| SP = ksp = ReadPQ (vms_hwpcb + 0); /* read stack ptrs */ | |
| esp = ReadPQ (vms_hwpcb + 8); | |
| ssp = ReadPQ (vms_hwpcb + 16); | |
| usp = ReadPQ (vms_hwpcb + 24); | |
| val = ReadPQ (vms_hwpcb + 32) << VA_N_OFF; /* read PTBR */ | |
| if (val != vms_ptbr) tlb_ia (TLB_CI | TLB_CD); /* if changed, zap TLB */ | |
| vms_ptbr = val; | |
| tmp = ReadPL (vms_hwpcb + 40) & M16; /* read ASN */ | |
| itlb_set_asn (tmp); | |
| dtlb_set_asn (tmp); | |
| tmp = ReadPL (vms_hwpcb + 48); /* read AST */ | |
| vms_astsr = (tmp >> 4) & AST_MASK; /* separate ASTSR, ASTEN */ | |
| vms_asten = tmp & AST_MASK; | |
| val = ReadPQ (vms_hwpcb + PCBV_FLAGS); /* read flags */ | |
| fpen = ((uint32) val) & 1; /* set FEN */ | |
| vms_datfx = ((uint32) (val >> 63)) & 1; /* set DATFX */ | |
| tmp = ReadL (vms_hwpcb + 64); | |
| pcc_h = (tmp - pcc_l) & M32; | |
| vms_thread = ReadPQ (vms_hwpcb + 72); /* read UNIQUE */ | |
| return; | |
| } | |
| /* VMS interrupt or exception | |
| Inputs: | |
| vec = SCB vector | |
| newmode = new mode (usually kernel) | |
| newipl = new IPL | |
| Outputs: | |
| reason = possible processor halt | |
| */ | |
| t_stat vms_intexc (uint32 vec, uint32 newmode, uint32 newipl) | |
| { | |
| t_uint64 pa = (vms_scbb + vec) & ~0xF; /* vector */ | |
| t_uint64 sav_ps = GET_PSV; /* old PS */ | |
| uint32 wacc = ACC_W (newmode); | |
| uint32 exc; | |
| vms_stkp[vms_cm] = SP; /* save SP */ | |
| SP = vms_stkp[newmode]; /* load new SP */ | |
| sav_ps = sav_ps | ((SP & PSV_M_SPA) << PSV_V_SPA); /* save SP align */ | |
| SP = SP & ~PSV_M_SPA; /* align SP */ | |
| SP = (SP - VMS_L_STKF) & M64; | |
| if (exc = Test (SP, wacc, NULL)) { /* check writes */ | |
| if (newmode == MODE_K) return STOP_KSNV; /* error? stop if kernel */ | |
| ABORT1 (SP, exc + EXC_W); /* else, force fault */ | |
| } | |
| if (exc = Test (SP + VMS_L_STKF - 8, wacc, NULL)) { | |
| if (newmode == MODE_K) return STOP_KSNV; | |
| ABORT1 (SP + VMS_L_STKF - 8, exc + EXC_W); | |
| } | |
| vms_cm = mmu_set_cm (newmode); /* switch mode */ | |
| WriteQ (SP, R[2]); /* save R2-R7 */ | |
| WriteQ (SP + 8, R[3]); | |
| WriteQ (SP + 16, R[4]); | |
| WriteQ (SP + 24, R[5]); | |
| WriteQ (SP + 32, R[6]); | |
| WriteQ (SP + 40, R[7]); | |
| WriteQ (SP + 48, PC); /* save PC */ | |
| WriteQ (SP + 56, sav_ps); /* save PS */ | |
| PC = R[2] = ReadPQ (pa); /* set new PC */ | |
| R[3] = ReadPQ (pa + 8); /* set argument */ | |
| vms_ipl = newipl; /* change IPL */ | |
| vms_ps = vms_ps & ~PSV_M_SW; | |
| return SCPE_OK; | |
| } | |
| /* Memory management fault */ | |
| t_stat vms_mm_intexc (uint32 vec, t_uint64 par2) | |
| { | |
| t_stat r; | |
| r = vms_intexc (vec, MODE_K, vms_ipl); /* take exception */ | |
| R[4] = p1; /* R[4] = va */ | |
| R[5] = par2; /* R[5] = MME */ | |
| tlb_is (p1, TLB_CI | TLB_CD); /* zap TLB entry */ | |
| return r; | |
| } | |
| /* Return from exception of interrupt */ | |
| t_stat vms_rei (void) | |
| { | |
| t_uint64 t1, t2, t3, t4, t5, t6, t7, t8; | |
| uint32 newmode; | |
| if (SP & PSV_M_SPA) ABORT (EXC_RSVO); /* check alignment */ | |
| if (vms_cm == MODE_K) { /* in kernel mode? */ | |
| if (Test (SP, cm_racc, NULL)) return STOP_KSNV; /* must be accessible */ | |
| if (Test (SP + VMS_L_STKF - 8, cm_racc, NULL)) return STOP_KSNV; | |
| } | |
| t1 = ReadQ (SP); /* pop stack */ | |
| t2 = ReadQ (SP + 8); | |
| t3 = ReadQ (SP + 16); | |
| t4 = ReadQ (SP + 24); | |
| t5 = ReadQ (SP + 32); | |
| t6 = ReadQ (SP + 40); | |
| t7 = ReadQ (SP + 48); | |
| t8 = ReadQ (SP + 56); | |
| newmode = (((uint32) t8) >> PSV_V_CM) && PSV_M_CM; /* get new mode */ | |
| if ((vms_cm != MODE_K) && /* not kernel? check new PS */ | |
| ((newmode < vms_cm) || (t8 & PSV_MBZ))) ABORT (EXC_RSVO); | |
| SP = (SP + VMS_L_STKF) | ((t8 >> PSV_V_SPA) & PSV_M_SPA); | |
| vms_stkp[vms_cm] = SP; /* save SP */ | |
| SP = vms_stkp[newmode]; /* load new SP */ | |
| R[2] = t1; /* restore R2-R7 */ | |
| R[3] = t2; | |
| R[4] = t3; | |
| R[5] = t4; | |
| R[6] = t5; | |
| R[7] = t6; | |
| PC = t7 & ~3; /* restore PC */ | |
| vms_ps = ((uint32) t8) & PSV_MASK; /* restore PS */ | |
| vms_cm = mmu_set_cm (newmode); /* switch modes */ | |
| vms_ipl = (((uint32) t8) >> PSV_V_IPL) & PSV_M_IPL; /* new IPL */ | |
| vax_flag = 0; /* clear vax, lock flags */ | |
| lock_flag = 0; | |
| return SCPE_OK; | |
| } | |
| /* Unaligned read virtual - for VMS PALcode only | |
| Inputs: | |
| va = virtual address | |
| lnt = length code (BWLQ) | |
| acc = access code (includes FOR, FOW) | |
| Output: | |
| returned data, right justified | |
| */ | |
| t_uint64 ReadUna (t_uint64 va, uint32 lnt, uint32 acc) | |
| { | |
| t_uint64 pa, pa1, wl, wh; | |
| uint32 exc, bo, sc; | |
| if (exc = Test (va, acc, &pa)) /* test, translate */ | |
| ABORT1 (va, exc + EXC_R); | |
| if ((pa & (lnt - 1)) == 0) { /* aligned? */ | |
| if (lnt == L_QUAD) return ReadPQ (pa); /* quad? */ | |
| if (lnt == L_LONG) return ReadPL (pa); /* long? */ | |
| if (lnt == L_WORD) return ReadPW (pa); /* word? */ | |
| return ReadPB (pa); /* byte */ | |
| } | |
| if ((VA_GETOFF (va) + lnt) > VA_PAGSIZE) { /* cross page? */ | |
| if (exc = Test (va + 8, acc, &pa1)) /* test, translate */ | |
| ABORT1 (va + 8, exc + EXC_R); | |
| } | |
| else pa1 = (pa + 8) & PA_MASK; /* not cross page */ | |
| bo = ((uint32) pa) & 7; /* byte in qw */ | |
| sc = bo << 3; /* shift count */ | |
| wl = ReadPQ (pa); /* get low qw */ | |
| if (lnt == L_QUAD) { /* qw unaligned? */ | |
| wh = ReadPQ (pa1); /* get high qw */ | |
| return ((((wl >> sc) & (((t_uint64) M64) >> sc)) | | |
| (wh << (64 - sc))) & M64); /* extract data */ | |
| } | |
| if (lnt == L_LONG) { /* lw unaligned? */ | |
| if (bo <= 4) return ((wl >> sc) & M32); /* all in one qw? */ | |
| wh = ReadPQ (pa1); /* get high qw */ | |
| return ((((wl >> sc) & (M32 >> (sc - 32))) | | |
| (wh << (64 - sc))) & M32); | |
| } | |
| if (bo < 7) return ((wl >> sc) & M16); /* wd, all in one qw? */ | |
| wh = ReadPQ (pa1); /* get hi qw, extract */ | |
| return (((wl >> 56) & 0xFF) | ((wh & 0xFF) << 8)); | |
| } | |
| /* Unaligned write virtual - for VMS PALcode only | |
| Inputs: | |
| va = virtual address | |
| val = data to be written, right justified in 64b | |
| lnt = length code (BWLQ) | |
| acc = access code (includes FOW) | |
| Output: | |
| none | |
| */ | |
| void WriteUna (t_uint64 va, t_uint64 val, uint32 lnt, uint32 acc) | |
| { | |
| t_uint64 pa, pa1, wl, wh, mask; | |
| uint32 exc, bo, sc; | |
| if (exc = Test (va, acc, &pa)) /* test, translate */ | |
| ABORT1 (va, exc + EXC_W); | |
| if ((pa & (lnt - 1)) == 0) { /* aligned? */ | |
| if (lnt == L_QUAD) WritePQ (pa, val); /* quad? */ | |
| else if (lnt == L_LONG) WritePL (pa, (uint32) val); /* long? */ | |
| else if (lnt == L_WORD) WritePW (pa, (uint32) val); /* word? */ | |
| else WritePB (pa, (uint32) val); /* byte */ | |
| return; | |
| } | |
| if ((VA_GETOFF (va) + lnt) > VA_PAGSIZE) { /* cross page? */ | |
| if (exc = Test (va + 8, acc, &pa1)) /* test, translate */ | |
| ABORT1 (va + 8, exc + EXC_W); | |
| } | |
| else pa1 = (pa + 8) & PA_MASK; /* not cross page */ | |
| bo = ((uint32) pa) & 7; /* byte in qw */ | |
| sc = bo << 3; /* shift count */ | |
| wl = ReadPQ (pa); /* get low qw */ | |
| if (lnt == L_QUAD) { /* qw unaligned? */ | |
| val = val & M64; /* mask data */ | |
| mask = ((t_uint64) M64) << sc; /* low qw mask */ | |
| wl = (wl & ~mask) | ((val << sc) & mask); /* insert low */ | |
| wh = ReadPQ (pa1); /* hi qw */ | |
| mask = ((t_uint64) M64) >> (64 - sc); /* hi qw mask */ | |
| wh = (wh & ~mask) | ((val >> (64 - sc)) & mask); | |
| WritePQ (pa, wl); /* write low */ | |
| WritePQ (pa, wh); /* write high */ | |
| } | |
| else if (lnt == L_LONG) { /* lw unaligned? */ | |
| val = val & M32; | |
| mask = ((t_uint64) M32) << sc; /* low qw mask */ | |
| wl = (wl & ~mask) | (val << sc); /* insert low */ | |
| WritePQ (pa, wl); /* write low */ | |
| if (bo >= 4) { /* 2nd qw? */ | |
| wh = ReadPQ (pa1); /* read hi qw */ | |
| mask = ((t_uint64) M32) >> (sc - 32); /* hi qw mask */ | |
| wh = (wh & ~mask) | (val >> (sc - 32)); /* insert high */ | |
| WritePQ (pa1, wh); /* write hi */ | |
| } | |
| } | |
| else { | |
| val = val & M16; /* mask data */ | |
| mask = ((t_uint64) M16) << sc; /* word, low qw mask */ | |
| wl = (wl & ~mask) | ((val & M16) << sc); /* insert low */ | |
| WritePQ (pa, wl); /* write low */ | |
| if (bo >= 7) { /* 2nd qw? */ | |
| wh = ReadPQ (pa1); /* read hi */ | |
| mask = 0xFF; /* hi qw mask */ | |
| wh = (wh & ~mask) | (val >> 8); /* insert high */ | |
| WritePQ (pa1, wh); /* write hi */ | |
| } | |
| } | |
| return; | |
| } | |
| /* Test the accessibility of an address (VMS and UNIX PALcode only) | |
| - In VMS, superpage is always 0 | |
| - In Unix, current mode is always kernel | |
| - Hence, superpages are always accessible */ | |
| uint32 Test (t_uint64 va, uint32 acc, t_uint64 *pa) | |
| { | |
| uint32 va_sext = VA_GETSEXT (va); | |
| uint32 vpn = VA_GETVPN (va); | |
| t_uint64 pte; | |
| uint32 exc; | |
| TLBENT *tlbp; | |
| if (!dmapen) { /* mapping off? */ | |
| if (pa) *pa = va & PA_MASK; /* pa = va */ | |
| return 0; | |
| } | |
| if ((va_sext != 0) && (va_sext != VA_M_SEXT)) /* invalid virt addr? */ | |
| return EXC_BVA; | |
| if ((mmu_dspage & SPEN_43) && (VPN_GETSP43 (vpn) == 2)) { | |
| if (pa) *pa = va & SP43_MASK; /* 43b superpage? */ | |
| return 0; | |
| } | |
| if ((mmu_dspage & SPEN_32) && (VPN_GETSP32 (vpn) == 0x1FFE)) { | |
| if (pa) *pa = va & SP32_MASK; /* 32b superpage? */ | |
| return 0; | |
| } | |
| if (!(tlbp = dtlb_lookup (vpn))) { /* lookup vpn; miss? */ | |
| if (exc = pal_find_pte (vpn, &pte)) return exc; /* fetch pte; error? */ | |
| tlbp = dtlb_load (vpn, pte); /* load new entry */ | |
| } | |
| if (acc & ~tlbp->pte) return mm_exc (acc & ~tlbp->pte); /* check access */ | |
| if (pa) *pa = PHYS_ADDR (tlbp->pfn, va); /* return phys addr */ | |
| return 0; /* ok */ | |
| } | |
| /* TLB check - VMS PALcode only */ | |
| uint32 tlb_check (t_uint64 va) | |
| { | |
| uint32 va_sext = VA_GETSEXT (va); | |
| uint32 vpn = VA_GETVPN (va); | |
| if ((va_sext != 0) && (va_sext != VA_M_SEXT)) return 0; | |
| if (itlb_lookup (vpn)) return 1; | |
| if (dtlb_lookup (vpn)) return 1; | |
| return 0; | |
| } | |
| /* VMS 3-level PTE lookup | |
| Inputs: | |
| vpn = virtual page number (30b, sext) | |
| *pte = pointer to pte to be returned | |
| Output: | |
| status = 0 for successful fill | |
| EXC_ACV for ACV on intermediate level | |
| EXC_TNV for TNV on intermediate level | |
| */ | |
| uint32 pal_find_pte_vms (uint32 vpn, t_uint64 *l3pte) | |
| { | |
| t_uint64 vptea, l1ptea, l2ptea, l3ptea, l1pte, l2pte; | |
| uint32 vpte_vpn; | |
| TLBENT *vpte_p; | |
| vptea = vms_vtbr | (((t_uint64) (vpn & VA_M_VPN)) << 3);/* try virtual lookup */ | |
| vpte_vpn = VA_GETVPN (vptea); /* get vpte vpn */ | |
| vpte_p = dtlb_lookup (vpte_vpn); /* get vpte tlb ptr */ | |
| if ((vpte_p->tag == vpte_vpn) && /* TLB hit? */ | |
| ((vpte_p->pte & (PTE_KRE|PTE_V)) == (PTE_KRE|PTE_V))) | |
| l3ptea = vpte_p->pfn | VA_GETOFF (vptea); | |
| else { | |
| l1ptea = vms_ptbr + VPN_GETLVL1 (vpn); | |
| l1pte = ReadPQ (l1ptea); | |
| if ((l1pte & PTE_V) == 0) | |
| return ((l1pte & PTE_KRE)? EXC_TNV: EXC_ACV); | |
| l2ptea = (l1pte & PFN_MASK) >> (PTE_V_PFN - VA_N_OFF); | |
| l2ptea = l2ptea + VPN_GETLVL2 (vpn); | |
| l2pte = ReadPQ (l2ptea); | |
| if ((l2pte & PTE_V) == 0) | |
| return ((l2pte & PTE_KRE)? EXC_TNV: EXC_ACV); | |
| l3ptea = (l2pte & PFN_MASK) >> (PTE_V_PFN - VA_N_OFF); | |
| l3ptea = l3ptea + VPN_GETLVL3 (vpn); | |
| } | |
| *l3pte = ReadPQ (l3ptea); | |
| return 0; | |
| } | |
| /* VMS PALcode reset */ | |
| t_stat pal_proc_reset_vms (DEVICE *dptr) | |
| { | |
| mmu_ispage = mmu_dspage = 0; | |
| vms_cm = mmu_set_cm (MODE_K); | |
| vms_ipl = IPL_1F; | |
| vms_ps = 0; | |
| vms_datfx = 0; | |
| vms_scbb = 0; | |
| vms_prbr = 0; | |
| vms_scc = 0; | |
| vms_last_pcc = pcc_l; | |
| pcc_enb = 1; | |
| pal_eval_intr = &pal_eval_intr_vms; | |
| pal_proc_intr = &pal_proc_intr_vms; | |
| pal_proc_trap = &pal_proc_trap_vms; | |
| pal_proc_excp = &pal_proc_excp_vms; | |
| pal_proc_inst = &pal_proc_inst_vms; | |
| pal_find_pte = &pal_find_pte_vms; | |
| return SCPE_OK; | |
| } |