1
0
mirror of https://github.com/simh/simh.git synced 2026-01-11 23:52:58 +00:00

PDP11: Fixing details in processor specific register behaviors, traps & aborts

This commit is contained in:
Walter Mueller 2022-12-06 11:08:47 -10:00 committed by Mark Pizzolato
parent 527cad6703
commit e46fc5cc56
3 changed files with 106 additions and 52 deletions

View File

@ -25,6 +25,12 @@
cpu PDP-11 CPU
30-Nov-22 RMS More 11/45,11/70 trap hackery (Walter Mueller)
29-Nov-22 RMS Trap stack abort must clear other traps/aborts (Walter Mueller)
23-Oct-22 RMS Fixed priority of MME traps (Walter Mueller)
02-Sep-22 RMS Fixed handling of PDR<A> (Walter Mueller)
31-Aug-22 RMS MMR0<15:13> != 0 locks bits<15:13> (Walter Mueller)
MMR0<12> = 1 disables further traps (Walter Mueller)
25-Aug-22 RMS 11/45,70 clear MMR1 in trap sequence (Walter Mueller)
23-Aug-22 RMS 11/45,70 detect red stack abort before memory write
in JSR, MFPx (Walter Mueller)
@ -382,22 +388,35 @@ extern int32 get_vector (int32 nipl);
/* Trap data structures */
int32 trap_vec[TRAP_V_MAX] = { /* trap req to vector */
VEC_RED, VEC_ODD, VEC_MME, VEC_NXM,
VEC_RED, VEC_ODD, VEC_NXM, VEC_MME,
VEC_PAR, VEC_PRV, VEC_ILL, VEC_BPT,
VEC_IOT, VEC_EMT, VEC_TRAP, VEC_TRC,
VEC_YEL, VEC_PWRFL, VEC_FPE
};
t_bool trap_load_mmr2[TRAP_V_MAX + 1] = { /* do trap requests load MMR2? */
TRUE, TRUE, TRUE, TRUE,
TRUE, FALSE, FALSE, FALSE,
FALSE, FALSE, FALSE, TRUE,
TRUE, TRUE, TRUE, TRUE /* last is interrupt */
};
int32 trap_clear[TRAP_V_MAX] = { /* trap clears */
TRAP_RED+TRAP_PAR+TRAP_YEL+TRAP_TRC+TRAP_ODD+TRAP_NXM,
TRAP_ODD+TRAP_PAR+TRAP_YEL+TRAP_TRC,
TRAP_MME+TRAP_PAR+TRAP_YEL+TRAP_TRC,
TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC,
TRAP_PAR+TRAP_TRC, TRAP_PRV+TRAP_TRC,
TRAP_ILL+TRAP_TRC, TRAP_BPT+TRAP_TRC,
TRAP_IOT+TRAP_TRC, TRAP_EMT+TRAP_TRC,
TRAP_TRAP+TRAP_TRC, TRAP_TRC,
TRAP_YEL, TRAP_PWRFL, TRAP_FPE
TRAP_RED+TRAP_ODD+TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC+TRAP_MME, /* red stack abort */
TRAP_ODD+TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC+TRAP_MME, /* odd address abort */
TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC+TRAP_MME, /* nxm abort */
TRAP_MME+TRAP_PAR+TRAP_YEL+TRAP_TRC, /* mme abort or trap */
TRAP_PAR+TRAP_YEL+TRAP_TRC,
TRAP_PRV+TRAP_TRC, /* instruction traps */
TRAP_ILL+TRAP_TRC, /* occur in fetch or */
TRAP_BPT+TRAP_TRC, /* initial decode */
TRAP_IOT+TRAP_TRC, /* no yelstk possible */
TRAP_EMT+TRAP_TRC,
TRAP_TRAP+TRAP_TRC,
TRAP_TRC,
TRAP_YEL,
TRAP_PWRFL,
TRAP_FPE
};
/* CPU data structures
@ -738,7 +757,7 @@ isenable = calc_is (cm);
dsenable = calc_ds (cm);
put_PIRQ (PIRQ); /* rewrite PIRQ */
STKLIM = STKLIM & STKLIM_RW; /* clean up STKLIM */
MMR0 = MMR0 | MMR0_IC; /* usually on */
MMR0 = MMR0 & ~MMR0_IC; /* usually off */
trap_req = calc_ints (ipl, trap_req); /* upd int req */
trapea = 0;
@ -800,7 +819,8 @@ else {
(CPUT (STOP_STKA) || stop_spabort))
reason = STOP_SPABORT;
if (trapea == ~MD_KER) { /* kernel stk abort? */
setTRAP (TRAP_RED);
trap_req = trap_req & ~trap_clear[TRAP_RED];/* clear all traps */
setTRAP (TRAP_RED); /* set red stack trap */
setCPUERR (CPUE_RED);
STACKFILE[MD_KER] = 4;
if (cm == MD_KER)
@ -888,6 +908,10 @@ while (reason == 0) {
6. Update SP, PSW, and PC
7. If not stack overflow, check for stack overflow
If the MMU registers are not frozen, the 11/45 and 11/70 will
also clear MMR1 and store the trap vector in MMR2, <except>
for the four instruction traps (EMT, TRAP, IOT, BPT).
If the reads in step 3, or the writes in step 5, match a data breakpoint,
the breakpoint status will be set but the interrupt actions will continue.
The breakpoint stop will occur at the beginning of the next instruction
@ -898,15 +922,13 @@ while (reason == 0) {
STACKFILE[cm] = SP;
PSW = get_PSW (); /* assemble PSW */
oldrs = rs;
if (CPUT (HAS_MMTR)) { /* 45,70? */
if (update_MM) { /* if not frozen */
MMR1 = 0; /* clear MMR1 */
if ((CPUT (HAS_MMTR)) && (update_MM)) { /* 45,70, not frozen? */
MMR1 = 0; /* clear MMR1 */
if (trap_load_mmr2[trapnum]) /* load MMR2? */
MMR2 = trapea; /* save vector */
}
MMR0 = MMR0 & ~MMR0_IC; /* clear IC */
}
src = ReadCW (trapea | calc_ds (MD_KER)); /* new PC */
src2 = ReadCW ((trapea + 2) | calc_ds (MD_KER)); /* new PSW */
src2 = ReadCW ((trapea + 2) | calc_ds (MD_KER));/* new PSW */
t = (src2 >> PSW_V_CM) & 03; /* new cm */
trapea = ~t; /* flag pushes */
WriteCW (PSW, ((STACKFILE[t] - 2) & 0177777) | calc_ds (t));
@ -928,7 +950,6 @@ while (reason == 0) {
if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y)) &&
(trapnum != TRAP_V_RED) && (trapnum != TRAP_V_YEL))
set_stack_trap (SP);
MMR0 = MMR0 | MMR0_IC; /* back to instr */
continue; /* end if traps */
}
@ -2899,14 +2920,16 @@ switch (apr & PDR_ACF) { /* case on ACF */
case 1: case 4: /* trap read */
if (CPUT (HAS_MMTR)) { /* traps implemented? */
APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */
if (MMR0 & MMR0_TENB) { /* traps enabled? */
int32 old_mmr0 = MMR0;
APRFILE[apridx] |= PDR_A; /* set A */
MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */
if ((MMR0 & MMR0_TENB) != 0) { /* traps enabled? */
if (update_MM) /* update MMR0 */
MMR0 = (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE);
MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */
setTRAP (TRAP_MME); /* set trap */
if ((old_mmr0 & MMR0_TRAP) == 0) /* first trap? */
setTRAP (TRAP_MME); /* set trap */
}
return; /* continue op */
return; /* continue */
} /* not impl, abort NR */
case 0: case 3: case 7: /* non-resident */
err = MMR0_NR; /* set MMR0 */
@ -2932,10 +2955,10 @@ return ((apr & PDR_ED)? (dbn < plf): (dbn > plf)); /* pg lnt error? */
void reloc_abort (int32 err, int32 apridx)
{
if (update_MM) MMR0 = /* update MMR0 */
(MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE);
APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */
MMR0 = MMR0 | err; /* set aborts */
if (update_MM) { /* MMR0 not frozen? */
MMR0 = (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE); /* record page */
MMR0 = MMR0 | err; /* OR in aborts */
}
ABORT (TRAP_MME); /* abort ref */
return;
}
@ -2967,7 +2990,7 @@ if (MMR0 & MMR0_MME) { /* if mmgt */
relocW_test (va, apridx); /* long test */
if (PLF_test (va, apr)) /* pg lnt error? */
reloc_abort (MMR0_PL, apridx);
APRFILE[apridx] = apr | PDR_W; /* set W */
APRFILE[apridx] |= PDR_W; /* set W */
pa = ((va & VA_DF) + ((apr >> 10) & 017777700)) & PAMASK;
if ((MMR3 & MMR3_M22E) == 0) {
pa = pa & 0777777;
@ -3007,14 +3030,16 @@ switch (apr & PDR_ACF) { /* case on ACF */
case 4: case 5: /* trap write */
if (CPUT (HAS_MMTR)) { /* traps implemented? */
APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */
if (MMR0 & MMR0_TENB) { /* traps enabled? */
int32 old_mmr0 = MMR0;
APRFILE[apridx] |= PDR_A; /* set PDR <A> */
MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */
if ((MMR0 & MMR0_TENB) != 0) { /* traps enabled? */
if (update_MM) /* update MMR0 */
MMR0 = (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE);
MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */
setTRAP (TRAP_MME); /* set trap */
if ((old_mmr0 & MMR0_TRAP) == 0) /* first trap? */
setTRAP (TRAP_MME); /* set trap */
}
return; /* continue op */
return; /* continue, set A */
} /* not impl, abort NR */
case 0: case 3: case 7: /* non-resident */
err = MMR0_NR; /* MMR0 status */

View File

@ -1,6 +1,6 @@
/* pdp11_cpumod.c: PDP-11 CPU model-specific features
Copyright (c) 2004-2020, Robert M Supnik
Copyright (c) 2004-2022, 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"),
@ -25,6 +25,7 @@
system PDP-11 model-specific registers
19-Nov-22 RMS Fixed byte access errors in PIRQ, STKLIM, CDR (Walter Mueller)
15-Sep-20 RMS Fixed problem in KDJ11E programmable clock (Paul Koning)
04-Mar-16 RMS Fixed maximum memory sizes to exclude IO page
14-Mar-16 RMS Modified to keep cpu_memsize in sync with MEMSIZE
@ -49,12 +50,21 @@
#include "pdp11_defs.h"
#include "pdp11_cpumod.h"
/* Byte write macros for system registers */
/* Byte write macros for system registers
EVN_IGN byte writes to the even byte are ignored
ODD_IGN byte writes to the odd byte are ignored
ODD_SHF byte writes to the odd byte zero the even byte
ODD_MRG byte writes write appropriate byte
*/
#define EVN_IGN(cur) \
if ((access == WRITEB) && ((pa & 1) == 0)) \
return SCPE_OK
#define ODD_IGN(cur) \
if ((access == WRITEB) && (pa & 1)) \
return SCPE_OK
#define ODD_WO(cur) \
#define ODD_SHF(cur) \
if ((access == WRITEB) && (pa & 1)) \
cur = cur << 8
#define ODD_MRG(prv,cur) \
@ -475,7 +485,8 @@ switch ((pa >> 1) & 017) { /* decode pa<4:1> */
return SCPE_OK;
case 015: /* PIRQ */
ODD_WO (data);
EVN_IGN (data);
ODD_SHF (data);
put_PIRQ (data);
return SCPE_OK;
}
@ -511,12 +522,14 @@ t_stat CPU45_wr (int32 data, int32 pa, int32 access)
switch ((pa >> 1) & 017) { /* decode pa<4:1> */
case 015: /* PIRQ */
ODD_WO (data);
EVN_IGN (data);
ODD_SHF (data);
put_PIRQ (data);
return SCPE_OK;
case 016: /* STKLIM */
ODD_WO (data);
EVN_IGN (data);
ODD_SHF (data);
STKLIM = data & STKLIM_RW;
return SCPE_OK;
} /* end switch pa */
@ -590,7 +603,8 @@ switch ((pa >> 1) & 017) { /* decode pa<4:1> */
return SCPE_OK;
case 016: /* STKLIM */
ODD_WO (data);
EVN_IGN (data);
ODD_SHF (data);
STKLIM = data & STKLIM_RW;
return SCPE_OK;
} /* end switch pa */
@ -661,12 +675,19 @@ switch ((pa >> 1) & 017) { /* decode pa<4:1> */
return SCPE_NXM; /* unimplemented */
}
/* From Walter Mueller: MEMERR is always written using the standard data
on the bus: the whole word for DATO, the high byte for DATOB odd,
and the low byte with DATOB even. The simulator always puts a byte
in the low 8 bits, so for an odd byte reference, it must be shifted
to the high byte.
*/
t_stat CPU70_wr (int32 data, int32 pa, int32 access)
{
switch ((pa >> 1) & 017) { /* decode pa<4:1> */
case 002: /* MEMERR */
ODD_WO (data);
ODD_SHF (data);
MEMERR = MEMERR & ~data;
return SCPE_OK;
@ -697,12 +718,14 @@ switch ((pa >> 1) & 017) { /* decode pa<4:1> */
return SCPE_OK;
case 015: /* PIRQ */
ODD_WO (data);
EVN_IGN (data);
ODD_SHF (data);
put_PIRQ (data);
return SCPE_OK;
case 016: /* STKLIM */
ODD_WO (data);
EVN_IGN (data);
ODD_SHF (data);
STKLIM = data & STKLIM_RW;
return SCPE_OK;
} /* end switch pa */
@ -779,7 +802,8 @@ switch ((pa >> 1) & 017) { /* decode pa<4:1> */
return SCPE_OK;
case 015: /* PIRQ */
ODD_WO (data);
EVN_IGN (data);
ODD_SHF (data);
put_PIRQ (data);
return SCPE_OK;
} /* end switch pa */
@ -818,12 +842,14 @@ switch ((pa >> 1) & 03) { /* decode pa<2:1> */
ODD_MRG (JPCR, data);
JPCR = data & PCRFB_RW;
return SCPE_OK;
case 1: /* MAINT */
ODD_MRG (MAINT, data);
MAINT = data;
return SCPE_OK;
case 2: /* CDR */
ODD_WO (data);
ODD_IGN (data);
DR = data & CDRFB_WR;
return SCPE_OK;
}
@ -881,7 +907,7 @@ switch ((pa >> 1) & 03) { /* decode pa<2:1> */
return SCPE_OK;
case 2: /* CDR */
ODD_WO (data);
ODD_IGN (data);
DR = data & CDRJB_WR;
return SCPE_OK;
}
@ -972,7 +998,7 @@ switch ((pa >> 1) & 03) { /* decode pa<2:1> */
return SCPE_OK;
case 2: /* CDR */
ODD_WO (data);
ODD_IGN (data);
DR = data & CDRJE_WR;
return SCPE_OK;

View File

@ -410,14 +410,17 @@ typedef struct {
#define CSR_BUSY (1u << CSR_V_BUSY)
#define CSR_ERR (1u << CSR_V_ERR)
/* Trap masks, descending priority order, following J-11
An interrupt summary bit is kept with traps, to minimize overhead
/* Trap masks, descending priority order. Rules:
- Aborts are mutually exclusive, no more than one per instrution.
- Aborts must be higher priority than traps. Because MME can be
either an abort or a trap, it is lower priority than NXM.
*/
#define TRAP_V_RED 0 /* red stk abort 4 */
#define TRAP_V_ODD 1 /* odd address 4 */
#define TRAP_V_MME 2 /* mem mgt 250 */
#define TRAP_V_NXM 3 /* nx memory 4 */
#define TRAP_V_NXM 2 /* nx memory 4 */
#define TRAP_V_MME 3 /* mem mgt 250 */
#define TRAP_V_PAR 4 /* parity err 114 */
#define TRAP_V_PRV 5 /* priv inst 4 */
#define TRAP_V_ILL 6 /* illegal inst 10 */