1
0
mirror of https://github.com/simh/simh.git synced 2026-02-06 16:35:43 +00:00

PDP11: CPU MMR1 and FP changes

There are a lot of niggling fixes, mostly for incompatibility issues
found by Walter Mueller. Working out the 11/70 behavior is still not
fully done, but it's a lot better than it was, and it passes more of the
11/70 MMU diagnostic.

- Floating point. Now model sensitive in treating MMR1, for 2.11 BSD.
- MMR1 now tracks PC changes on 11/44, 11/45, 11/70, and J11 -(PC) and @-(PC).
- MMR1 is cleared at start of trap sequence on 11/45, 11/70.
- Red stack abort occurs before memory writes on 11/45, 11/70 (no other model has them).
This commit is contained in:
Bob Supnik
2022-09-09 19:27:37 -07:00
committed by Mark Pizzolato
parent 7fcaa7cf02
commit 72f75ec1b5
2 changed files with 105 additions and 50 deletions

View File

@@ -1,6 +1,6 @@
/* pdp11_cpu.c: PDP-11 CPU simulator
Copyright (c) 1993-2016, Robert M Supnik
Copyright (c) 1993-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,14 @@
cpu PDP-11 CPU
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)
20-Aug-22 RMS MMR1 reads as 0 on subset memory mmgt systems
11/44, 45, 70 track PC changes (Walter Mueller)
J11 tracks PC changes on -(PC) and @-(PC)
25-Jul-22 RMS Removed deprecated CPU models (Q22, UHR11, URH70)
04-Jun-18 RMS Removed CPU model entries for UC15 (Mark Pizzolato)
04-Dec-16 RMS Removed duplicate IDLE entries in MTAB
30-Aug-16 RMS Fixed overloading of -d in ex/mod
14-Mar-16 RMS Added UC15 support
@@ -338,6 +346,7 @@ int32 relocR (int32 addr);
int32 relocW (int32 addr);
void relocR_test (int32 va, int32 apridx);
void relocW_test (int32 va, int32 apridx);
int32 clean_MMR1 (int32 mmr1);
t_bool PLF_test (int32 va, int32 apr);
void reloc_abort (int32 err, int32 apridx);
int32 ReadE (int32 addr);
@@ -609,9 +618,9 @@ MTAB cpu_mod[] = {
{ MTAB_XTD|MTAB_VDV, MOD_1184, NULL, "11/84", &cpu_set_model, NULL, NULL, "Set CPU type to 11/84" },
{ MTAB_XTD|MTAB_VDV, MOD_1193, NULL, "11/93", &cpu_set_model, NULL, NULL, "Set CPU type to 11/93" },
{ MTAB_XTD|MTAB_VDV, MOD_1194, NULL, "11/94", &cpu_set_model, NULL, NULL, "Set CPU type to 11/94" },
{ MTAB_XTD|MTAB_VDV, MOD_1173, NULL, "Q22", &cpu_set_model, NULL, NULL, "deprecated: same as 11/73" },
{ MTAB_XTD|MTAB_VDV, MOD_1184, NULL, "URH11", &cpu_set_model, NULL, NULL, "deprecated: same as 11/84" },
{ MTAB_XTD|MTAB_VDV, MOD_1170, NULL, "URH70", &cpu_set_model, NULL, NULL, "deprecated: same as 11/70" },
// { MTAB_XTD|MTAB_VDV, MOD_1173, NULL, "Q22", &cpu_set_model, NULL, NULL, "deprecated: same as 11/73" },
// { MTAB_XTD|MTAB_VDV, MOD_1184, NULL, "URH11", &cpu_set_model, NULL, NULL, "deprecated: same as 11/84" },
// { MTAB_XTD|MTAB_VDV, MOD_1170, NULL, "URH70", &cpu_set_model, NULL, NULL, "deprecated: same as 11/70" },
{ MTAB_XTD|MTAB_VDV, MOD_1145, NULL, "U18", &cpu_set_model, NULL, NULL, "deprecated: same as 11/45" },
{ MTAB_XTD|MTAB_VDV, OPT_EIS, NULL, "EIS", &cpu_set_opt, NULL, NULL, "enable EIS instructions" },
{ MTAB_XTD|MTAB_VDV, OPT_EIS, NULL, "NOEIS", &cpu_clr_opt, NULL, NULL, "disable EIS instructions" },
@@ -888,8 +897,10 @@ while (reason == 0) {
PSW = get_PSW (); /* assemble PSW */
oldrs = rs;
if (CPUT (HAS_MMTR)) { /* 45,70? */
if (update_MM) /* save vector */
MMR2 = trapea;
if (update_MM) { /* if not frozen */
MMR1 = 0; /* clear MMR1 */
MMR2 = trapea; /* save vector */
}
MMR0 = MMR0 & ~MMR0_IC; /* clear IC */
}
src = ReadCW (trapea | calc_ds (MD_KER)); /* new PC */
@@ -1222,9 +1233,9 @@ while (reason == 0) {
reg_mods = calc_MMR1 (0366);
if (update_MM)
MMR1 = reg_mods;
WriteW (R[srcspec], SP | dsenable);
if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y)))
set_stack_trap (SP);
WriteW (R[srcspec], SP | dsenable);
R[srcspec] = PC;
if (hst_ent)
hst_ent->dst = dst;
@@ -1424,9 +1435,9 @@ while (reason == 0) {
MMR1 = reg_mods;
if (hst_ent)
hst_ent->dst = dst;
WriteW (dst, SP | dsenable);
if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y)))
set_stack_trap (SP);
WriteW (dst, SP | dsenable);
}
else setTRAP (TRAP_ILL);
break;
@@ -1715,7 +1726,7 @@ while (reason == 0) {
break;
}
if ((((uint32)src) == 020000000000) && (src2 == 0177777)) {
V = 1; /* J11,11/70 compat */
V = 1; /* V = 1 */
N = Z = C = 0; /* N = Z = 0 */
break;
}
@@ -1730,7 +1741,7 @@ while (reason == 0) {
}
N = (dst < 0); /* N set on 32b result */
if ((dst > 077777) || (dst < -0100000)) {
V = 1; /* J11,11/70 compat */
V = 1; /* V = 1 */
Z = C = 0; /* Z = C = 0 */
break;
}
@@ -2215,9 +2226,9 @@ while (reason == 0) {
MMR1 = reg_mods;
if (hst_ent)
hst_ent->dst = dst;
WriteW (dst, SP | dsenable);
if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y)))
set_stack_trap (SP);
WriteW (dst, SP | dsenable);
}
else setTRAP (TRAP_ILL);
break;
@@ -2414,6 +2425,7 @@ for (i = 0; i < 6; i++)
REGFILE[i][rs] = R[i];
STACKFILE[cm] = SP;
saved_PC = PC & 0177777;
MMR1 = clean_MMR1 (MMR1); /* clean up MMR1 */
pcq_r->qptr = pcq_p; /* update pc q ptr */
set_r_display (rs, cm);
return reason;
@@ -2454,7 +2466,7 @@ return reason;
explicitly reference the PC. For the J-11, this is only true for
autodecrement operands, autodecrement deferred operands, and
autoincrement destination operands that involve a write to memory.
The simulator follows the Handbook, for simplicity.
This is cleaned up at simulator exit or MMR1 read.
Notes:
@@ -2482,14 +2494,14 @@ switch (spec >> 3) { /* decode spec<5:3> */
case 2: /* (R)+ */
R[reg] = ((adr = R[reg]) + 2) & 0177777;
reg_mods = calc_MMR1 (020 | reg);
if (update_MM && (reg != 7))
if (update_MM)
MMR1 = reg_mods;
return (adr | ds);
case 3: /* @(R)+ */
R[reg] = ((adr = R[reg]) + 2) & 0177777;
reg_mods = calc_MMR1 (020 | reg);
if (update_MM && (reg != 7))
if (update_MM)
MMR1 = reg_mods;
adr = ReadW (adr | ds);
return (adr | dsenable);
@@ -2497,7 +2509,7 @@ switch (spec >> 3) { /* decode spec<5:3> */
case 4: /* -(R) */
adr = R[reg] = (R[reg] - 2) & 0177777;
reg_mods = calc_MMR1 (0360 | reg);
if (update_MM && (reg != 7))
if (update_MM)
MMR1 = reg_mods;
if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y)))
set_stack_trap (adr);
@@ -2506,7 +2518,7 @@ switch (spec >> 3) { /* decode spec<5:3> */
case 5: /* @-(R) */
adr = R[reg] = (R[reg] - 2) & 0177777;
reg_mods = calc_MMR1 (0360 | reg);
if (update_MM && (reg != 7))
if (update_MM)
MMR1 = reg_mods;
if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y)))
set_stack_trap (adr);
@@ -2551,7 +2563,7 @@ switch (spec >> 3) { /* decode spec<5:3> */
case 3: /* @(R)+ */
R[reg] = ((adr = R[reg]) + 2) & 0177777;
reg_mods = calc_MMR1 (020 | reg);
if (update_MM && (reg != 7))
if (update_MM)
MMR1 = reg_mods;
adr = ReadW (adr | ds);
return (adr | dsenable);
@@ -2560,7 +2572,7 @@ switch (spec >> 3) { /* decode spec<5:3> */
delta = 1 + (reg >= 6); /* 2 if R6, PC */
adr = R[reg] = (R[reg] - delta) & 0177777;
reg_mods = calc_MMR1 ((((-delta) & 037) << 3) | reg);
if (update_MM && (reg != 7))
if (update_MM)
MMR1 = reg_mods;
if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y)))
set_stack_trap (adr);
@@ -2569,7 +2581,7 @@ switch (spec >> 3) { /* decode spec<5:3> */
case 5: /* @-(R) */
adr = R[reg] = (R[reg] - 2) & 0177777;
reg_mods = calc_MMR1 (0360 | reg);
if (update_MM && (reg != 7))
if (update_MM)
MMR1 = reg_mods;
if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y)))
set_stack_trap (adr);
@@ -3087,7 +3099,8 @@ switch ((pa >> 1) & 3) { /* decode pa<2:1> */
break;
case 2: /* MMR1 */
*data = MMR1;
MMR1 = clean_MMR1 (MMR1); /* clean up MMR1 */
*data = MMR1; /* return data */
break;
case 3: /* MMR2 */
@@ -3133,6 +3146,29 @@ dsenable = calc_ds (cm);
return SCPE_OK;
}
/* Clean up MMR1 for presentation
!HAS_SID MMR1 is 0
HAS_SID && J11 MMR1 values corresponding to # and @# are cleared
HAS_SID && !J11 MMR1 is unchanged
Note that # and @# always generate reg = 7 and change = 2;
no other specifier combination can do that.
*/
int32 clean_MMR1 (int32 mmr1)
{
if (!CPUT (HAS_SID)) /* not full mmgt? */
return 0; /* always 0 */
if (CPUT (CPUT_J)) { /* J11? */
if ((mmr1 >> 8) == 027) /* high byte # or @#? */
mmr1 = mmr1 & 0377; /* erase high byte */
if ((mmr1 & 0377) == 027) /* low byte # or @#? */
mmr1 = mmr1 >> 8; /* erase low byte */
}
return mmr1;
}
/* PARs and PDRs. These are grouped in I/O space as follows:
17772200 - 17772276 supervisor block

View File

@@ -1,6 +1,6 @@
/* pdp11_fp.c: PDP-11 floating point simulator (32b version)
Copyright (c) 1993-2015, Robert M Supnik
Copyright (c) 1993-2018, 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"),
@@ -23,6 +23,7 @@
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
21-Aug-22 RMS Restored MMR1 operation for 11/44, 11/45-70 (Walter Mueller)
24-Mar-15 RMS MMR1 does not track register changes (Johnny Billquist)
20-Apr-13 RMS MMR1 does not track PC changes (Johnny Billquist)
22-Sep-05 RMS Fixed declarations (Sterling Garwood)
@@ -248,9 +249,10 @@ static const uint32 and_mask[33] = { 0,
int32 backup_PC;
int32 fp_change;
int32 fpnotrap (int32 code);
t_bool fpnotrap (int32 code);
int32 GeteaFW (int32 spec);
int32 GeteaFP (int32 spec, int32 len);
void fp_reg_change (int32 len, int32 reg);
uint32 ReadI (int32 addr, int32 spec, int32 len);
t_bool ReadFP (fpac_t *fac, int32 addr, int32 spec, int32 len);
void WriteI (int32 data, int32 addr, int32 spec, int32 len);
@@ -606,33 +608,25 @@ switch (spec >> 3) { /* decode spec<5:3> */
case 2: /* (R)+ */
adr = R[reg]; /* post increment */
if (reg == 7) /* commit PC chg now */
R[reg] = (R[reg] + 2) & 0177777;
else fp_change = FPCHG (2, reg); /* others, update later */
fp_reg_change (2, reg); /* update */
return (adr | ds);
case 3: /* @(R)+ */
adr = R[reg]; /* post increment */
if (reg == 7) /* commit PC chg now */
R[reg] = (R[reg] + 2) & 0177777;
else fp_change = FPCHG (2, reg); /* others, update later */
fp_reg_change (2, reg); /* update */
adr = ReadW (adr | ds);
return (adr | dsenable);
case 4: /* -(R) */
adr = (R[reg] - 2) & 0177777; /* predecrement */
if (reg == 7) /* commit PC chg now */
R[reg] = adr;
else fp_change = FPCHG (-2, reg); /* others, update later */
fp_reg_change (-2, reg); /* update */
if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y)))
set_stack_trap (adr);
return (adr | ds);
case 5: /* @-(R) */
adr = (R[reg] - 2) & 0177777; /* predecrement */
if (reg == 7) /* commit PC chg now */
R[reg] = adr;
else fp_change = FPCHG (-2, reg); /* others, update later */
fp_reg_change (-2, reg); /* update */
if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y)))
set_stack_trap (adr);
adr = ReadW (adr | ds);
@@ -687,33 +681,27 @@ switch (spec >> 3) { /* case on spec */
case 2: /* (R)+ */
adr = R[reg]; /* post increment */
if (reg == 7) /* commit PC chg now */
R[reg] = (R[reg] + 2) & 0177777;
else fp_change = FPCHG (len, reg); /* others, update later */
if (reg == 7) /* # is always length 2 */
len = 2;
fp_reg_change (len, reg); /* update */
return (adr | ds);
case 3: /* @(R)+ */
adr = R[reg]; /* post increment */
if (reg == 7) /* commit PC chg now */
R[reg] = (R[reg] + 2) & 0177777;
else fp_change = FPCHG (2, reg); /* others, update later */
fp_reg_change (2, reg); /* update */
adr = ReadW (adr | ds);
return (adr | dsenable);
case 4: /* -(R) */
adr = (R[reg] - len) & 0177777; /* predecrement */
if (reg == 7) /* commit PC chg now */
R[reg] = adr;
else fp_change = FPCHG (-len, reg); /* others, udpate later */
fp_reg_change (-len, reg); /* update */
if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y)))
set_stack_trap (adr);
return (adr | ds);
case 5: /* @-(R) */
adr = (R[reg] - 2) & 0177777; /* predecrement */
if (reg == 7) /* commit PC chg now */
R[reg] = adr;
else fp_change = FPCHG (-2, reg); /* others, update later */
fp_reg_change (-2, reg); /* update */
if ((reg == 6) && (cm == MD_KER) && ((adr - 2) < (STKLIM + STKL_Y)))
set_stack_trap (adr);
adr = ReadW (adr | ds);
@@ -734,6 +722,35 @@ switch (spec >> 3) { /* case on spec */
return 0;
}
/* Specifier register change
On systems with full memory management, the 11/44, 11/45, and 11/70
operate differently than J11. The former do normal register modification
and track changes in MMR1; on an abort, the register modifications
are visible. The J11 does not perform normal register modification
and tracking. Instead, it tracks changes internally and only updates
the general registers upon successful completion of the instruction.
On an abort, the general registers are unchanged.
This routine performs the appropriate bookkeeping for the different
models.
*/
void fp_reg_change (int32 len, int32 reg)
{
if (CPUT (CPUT_J)) { /* J11? */
if (reg == 7)
R[reg] = (R[reg] + len) & 0177777; /* commit PC changes now */
else fp_change = FPCHG (len, reg); /* track other changes */
}
else { /* all others */
R[reg] = (R[reg] + len) & 0177777; /* commit reg changes */
if (update_MM) /* if not frozen */
MMR1 = ((len & 037) << 3) | reg; /* update MMR1 */
}
return;
}
/* Read integer operand
Inputs:
@@ -785,10 +802,12 @@ else {
(ReadW (exta | ((VA + 6) & 0177777)) << FP_V_F3);
else fptr->l = 0;
}
if ((GET_SIGN (fptr->h) != 0) &&
if ((GET_SIGN (fptr->h) != 0) && /* undef variable? */
(GET_EXP (fptr->h) == 0) &&
(fpnotrap (FEC_UNDFV) == 0))
return FALSE;
!fpnotrap (FEC_UNDFV)) { /* trap enabled? */
fp_change = 0; /* J11, no reg changes */
return FALSE; /* NOP instruction */
}
return TRUE;
}
@@ -1393,7 +1412,7 @@ return 0;
int = FALSE if interrupt enabled, TRUE if disabled
*/
int32 fpnotrap (int32 code)
t_bool fpnotrap (int32 code)
{
static const int32 test_code[] = { 0, 0, 0, FPS_IC, FPS_IV, FPS_IU, FPS_IUV };