blob: 5d7d1bdc5044d67b777f1f5c4ece0c91b8d95c93 [file] [log] [blame] [raw]
/* 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;
}