From 89ffed467faa8ac41118f020fb76f61d1e9086b8 Mon Sep 17 00:00:00 2001 From: Paul Koning Date: Tue, 13 Sep 2016 15:22:35 -0400 Subject: [PATCH] PDP11: Add new breakpoint types for data and physical address Add support for physical and virtual address variants for the existing instruction (PC) breakpoint, and add memory read and memory write breakpoints. --- PDP11/pdp11_cpu.c | 338 ++++++++++++++++++++++++++++++--------------- PDP11/pdp11_defs.h | 22 +++ PDP11/pdp11_fp.c | 90 ++++++++++-- doc/pdp11_doc.doc | Bin 185856 -> 191488 bytes 4 files changed, 321 insertions(+), 129 deletions(-) diff --git a/PDP11/pdp11_cpu.c b/PDP11/pdp11_cpu.c index 95e5adea..df08b28e 100644 --- a/PDP11/pdp11_cpu.c +++ b/PDP11/pdp11_cpu.c @@ -233,14 +233,15 @@ #define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC #define calc_is(md) ((md) << VA_V_MODE) #define calc_ds(md) (calc_is((md)) | ((MMR3 & dsmask[(md)])? VA_DS: 0)) -#define calc_MMR1(val) ((MMR1)? (((val) << 8) | MMR1): (val)) +/* Register change tracking actually goes into variable reg_mods; from there + it is copied into MMR1 if that register is not currently locked. */ +#define calc_MMR1(val) ((reg_mods)? (((val) << 8) | reg_mods): (val)) #define GET_SIGN_W(v) (((v) >> 15) & 1) #define GET_SIGN_B(v) (((v) >> 7) & 1) #define GET_Z(v) ((v) == 0) #define JMP_PC(x) PCQ_ENTRY; PC = (x) #define BRANCH_F(x) PCQ_ENTRY; PC = (PC + (((x) + (x)) & 0377)) & 0177777 #define BRANCH_B(x) PCQ_ENTRY; PC = (PC + (((x) + (x)) | 0177400)) & 0177777 -#define last_pa (cpu_unit.u4) /* auto save/rest */ #define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy */ #define UNIT_MSIZE (1u << UNIT_V_MSIZE) @@ -306,6 +307,12 @@ int32 hst_lnt = 0; /* history length */ InstHistory *hst = NULL; /* instruction history */ int32 dsmask[4] = { MMR3_KDS, MMR3_SDS, 0, MMR3_UDS }; /* dspace enables */ t_addr cpu_memsize = INIMEMSIZE; /* last mem addr */ +int16 inst_pc; /* PC of current instr */ +int32 inst_psw; /* PSW at instr. start */ +int16 reg_mods; /* reg deltas */ +int32 last_pa; /* pa from ReadMW/ReadMB */ +int32 saved_sim_interval; /* saved at inst start */ +t_stat reason; /* stop reason */ extern int32 CPUERR, MAINT; extern CPUTAB cpu_tab[]; @@ -330,10 +337,14 @@ void reloc_abort (int32 err, int32 apridx); int32 ReadE (int32 addr); int32 ReadW (int32 addr); int32 ReadB (int32 addr); +int32 ReadCW (int32 addr); int32 ReadMW (int32 addr); int32 ReadMB (int32 addr); +int32 PReadW (int32 addr); +int32 PReadB (int32 addr); void WriteW (int32 data, int32 addr); void WriteB (int32 data, int32 addr); +void WriteCW (int32 data, int32 addr); void PWriteW (int32 data, int32 addr); void PWriteB (int32 data, int32 addr); void set_r_display (int32 rs, int32 cm); @@ -655,6 +666,16 @@ DEVICE cpu_dev = { NULL, &cpu_set_size, NULL }; +BRKTYPTAB cpu_breakpoints [] = { + BRKTYPE('E',"Execute Instruction at Virtual Address"), + BRKTYPE('P',"Execute Instruction at Physical Address"), + BRKTYPE('R',"Read from Virtual Address"), + BRKTYPE('S',"Read from Physical Address"), + BRKTYPE('W',"Write to Virtual Address"), + BRKTYPE('X',"Write to Physical Address"), + { 0 } + }; + t_value pdp11_pc_value (void) { return (t_value)PC; @@ -664,7 +685,6 @@ t_stat sim_instr (void) { int abortval, i; volatile int32 trapea; /* used by setjmp */ -t_stat reason; InstHistory *hst_ent = NULL; sim_vm_pc_value = &pdp11_pc_value; @@ -719,19 +739,50 @@ reason = 0; */ abortval = setjmp (save_env); /* set abort hdlr */ -if (abortval != 0) { - trap_req = trap_req | abortval; /* or in trap flag */ - if ((trapea > 0) && stop_vecabort) - reason = STOP_VECABORT; - if ((trapea < 0) && /* stack push abort? */ - (CPUT (STOP_STKA) || stop_spabort)) - reason = STOP_SPABORT; - if (trapea == ~MD_KER) { /* kernel stk abort? */ - setTRAP (TRAP_RED); - setCPUERR (CPUE_RED); - STACKFILE[MD_KER] = 4; - if (cm == MD_KER) - SP = 4; +if (abortval == ABRT_BKPT) { + /* Breakpoint encountered. */ + reason = STOP_IBKPT; + /* Print a message reporting the type and address if it is not a + plain virtual PC (instruction execution) breakpoint. */ + if (sim_brk_match_type != BPT_PCVIR) + sim_messagef (reason, "\r\n%s", sim_brk_message()); + /* Restore the PC and sim_interval. */ + PC = inst_pc; + sim_interval = saved_sim_interval; + /* Restore PSW and the broken-out condition code values, provided + FPD is not currently set. If it is, that means the instruction + is interruptible and breakpoints are treated as continuation + rather than replay. */ + if (!fpd) { + PSW = inst_psw; + put_PSW (inst_psw, 0); + } + /* Undo register changes. */ + while (reg_mods) { + int rnum = reg_mods & 7; + int delta = (reg_mods >> 3) & 037; + reg_mods >>= 8; + if (delta & 020) /* negative delta */ + delta = -(-delta & 037); /* get signed value */ + if (rnum != 7) + R[rnum] -= delta; + } + } +else { + if (abortval != 0) { + trap_req = trap_req | abortval; /* or in trap flag */ + if ((trapea > 0) && stop_vecabort) + reason = STOP_VECABORT; + if ((trapea < 0) && /* stack push abort? */ + (CPUT (STOP_STKA) || stop_spabort)) + reason = STOP_SPABORT; + if (trapea == ~MD_KER) { /* kernel stk abort? */ + setTRAP (TRAP_RED); + setCPUERR (CPUE_RED); + STACKFILE[MD_KER] = 4; + if (cm == MD_KER) + SP = 4; + } } } @@ -813,6 +864,11 @@ while (reason == 0) { 5. Push the old PC and PSW on the new stack 6. Update SP, PSW, and PC 7. If not stack overflow, check for stack overflow + + 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 + cycle. */ wait_state = 0; /* exit wait state */ @@ -824,12 +880,12 @@ while (reason == 0) { MMR2 = trapea; MMR0 = MMR0 & ~MMR0_IC; /* clear IC */ } - src = ReadW (trapea | calc_ds (MD_KER)); /* new PC */ - src2 = ReadW ((trapea + 2) | calc_ds (MD_KER)); /* new PSW */ + src = ReadCW (trapea | calc_ds (MD_KER)); /* new PC */ + src2 = ReadCW ((trapea + 2) | calc_ds (MD_KER)); /* new PSW */ t = (src2 >> PSW_V_CM) & 03; /* new cm */ trapea = ~t; /* flag pushes */ - WriteW (PSW, ((STACKFILE[t] - 2) & 0177777) | calc_ds (t)); - WriteW (PC, ((STACKFILE[t] - 4) & 0177777) | calc_ds (t)); + WriteCW (PSW, ((STACKFILE[t] - 2) & 0177777) | calc_ds (t)); + WriteCW (PC, ((STACKFILE[t] - 4) & 0177777) | calc_ds (t)); trapea = 0; /* clear trap flag */ src2 = (src2 & ~PSW_PM) | (cm << PSW_V_PM); /* insert prv mode */ put_PSW (src2, 0); /* call calc_is,ds */ @@ -860,14 +916,20 @@ while (reason == 0) { continue; } - if (sim_brk_summ) { /* breakpoint? */ - int32 pa = PC; /* FixMe */ - - if (sim_brk_test (PC, SWMASK ('E')) || /* Normal PC breakpoint? */ - sim_brk_test (pa, SWMASK ('P'))) { /* Physical Address breakpoint? */ - reason = STOP_IBKPT; /* stop simulation */ - continue; - } + reg_mods = 0; + inst_pc = PC; + /* Save PSW also because condition codes need to be preserved. + We just save the whole PSW because that is sufficient (that + representation is up to date at this point). If restoring is + needed, both the PSW and the components that need to be restored + are handled explicitly. */ + inst_psw = PSW; + saved_sim_interval = sim_interval; + if (BPT_SUMM_PC) { /* possible breakpoint */ + t_addr pa = relocR (PC | isenable); /* relocate PC */ + if (sim_brk_test (PC, BPT_PCVIR) || /* Normal PC breakpoint? */ + sim_brk_test (pa, BPT_PCPHY)) /* Physical Address breakpoint? */ + ABORT (ABRT_BKPT); /* stop simulation */ } if (update_MM) { /* if mm not frozen */ @@ -1145,8 +1207,9 @@ while (reason == 0) { ((dstspec & 070) == 020)) /* JSR (R)+? */ dst = R[dstspec & 07]; /* use post incr */ SP = (SP - 2) & 0177777; + reg_mods = calc_MMR1 (0366); if (update_MM) - MMR1 = calc_MMR1 (0366); + MMR1 = reg_mods; WriteW (R[srcspec], SP | dsenable); if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y))) set_stack_trap (SP); @@ -1344,8 +1407,9 @@ while (reason == 0) { Z = GET_Z (dst); V = 0; SP = (SP - 2) & 0177777; + reg_mods = calc_MMR1 (0366); if (update_MM) - MMR1 = calc_MMR1 (0366); + MMR1 = reg_mods; if (hst_ent) hst_ent->dst = dst; WriteW (dst, SP | dsenable); @@ -1362,7 +1426,8 @@ while (reason == 0) { Z = GET_Z (dst); V = 0; SP = (SP + 2) & 0177777; - if (update_MM) MMR1 = 026; + reg_mods = 026; + if (update_MM) MMR1 = reg_mods; if (hst_ent) hst_ent->dst = dst; if (dstreg) { @@ -2133,8 +2198,9 @@ while (reason == 0) { Z = GET_Z (dst); V = 0; SP = (SP - 2) & 0177777; + reg_mods = calc_MMR1 (0366); if (update_MM) - MMR1 = calc_MMR1 (0366); + MMR1 = reg_mods; if (hst_ent) hst_ent->dst = dst; WriteW (dst, SP | dsenable); @@ -2151,8 +2217,9 @@ while (reason == 0) { Z = GET_Z (dst); V = 0; SP = (SP + 2) & 0177777; + reg_mods = 026; if (update_MM) - MMR1 = 026; + MMR1 = reg_mods; if (hst_ent) hst_ent->dst = dst; if (dstreg) { @@ -2402,29 +2469,33 @@ 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)) - MMR1 = calc_MMR1 (020 | reg); + 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)) - MMR1 = calc_MMR1 (020 | reg); + MMR1 = reg_mods; adr = ReadW (adr | ds); return (adr | dsenable); case 4: /* -(R) */ adr = R[reg] = (R[reg] - 2) & 0177777; + reg_mods = calc_MMR1 (0360 | reg); if (update_MM && (reg != 7)) - MMR1 = calc_MMR1 (0360 | reg); + MMR1 = reg_mods; if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) set_stack_trap (adr); return (adr | ds); case 5: /* @-(R) */ adr = R[reg] = (R[reg] - 2) & 0177777; + reg_mods = calc_MMR1 (0360 | reg); if (update_MM && (reg != 7)) - MMR1 = calc_MMR1 (0360 | reg); + MMR1 = reg_mods; if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) set_stack_trap (adr); adr = ReadW (adr | ds); @@ -2460,30 +2531,34 @@ switch (spec >> 3) { /* decode spec<5:3> */ case 2: /* (R)+ */ delta = 1 + (reg >= 6); /* 2 if R6, PC */ R[reg] = ((adr = R[reg]) + delta) & 0177777; + reg_mods = calc_MMR1 ((delta << 3) | reg); if (update_MM && (reg != 7)) - MMR1 = calc_MMR1 ((delta << 3) | reg); + 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)) - MMR1 = calc_MMR1 (020 | reg); + MMR1 = reg_mods; adr = ReadW (adr | ds); return (adr | dsenable); case 4: /* -(R) */ 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)) - MMR1 = calc_MMR1 ((((-delta) & 037) << 3) | reg); + MMR1 = reg_mods; if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) set_stack_trap (adr); return (adr | ds); case 5: /* @-(R) */ adr = R[reg] = (R[reg] - 2) & 0177777; + reg_mods = calc_MMR1 (0360 | reg); if (update_MM && (reg != 7)) - MMR1 = calc_MMR1 (0360 | reg); + MMR1 = reg_mods; if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) set_stack_trap (adr); adr = ReadW (adr | ds); @@ -2519,6 +2594,10 @@ if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ ABORT (TRAP_ODD); } pa = relocR (va); /* relocate */ +if (BPT_SUMM_RD && + (sim_brk_test (va & 0177777, BPT_RDVIR) || + sim_brk_test (pa, BPT_RDPHY))) /* read breakpoint? */ + ABORT (ABRT_BKPT); /* stop simulation */ if (ADDR_IS_MEM (pa)) /* memory address? */ return (M[pa >> 1]); if ((pa < IOPAGEBASE) || /* not I/O address */ @@ -2535,13 +2614,79 @@ return data; int32 ReadW (int32 va) { -int32 pa, data; +int32 pa; if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ setCPUERR (CPUE_ODD); ABORT (TRAP_ODD); } pa = relocR (va); /* relocate */ +if (BPT_SUMM_RD && + (sim_brk_test (va & 0177777, BPT_RDVIR) || + sim_brk_test (pa, BPT_RDPHY))) /* read breakpoint? */ + ABORT (ABRT_BKPT); /* stop simulation */ +return PReadW (pa); +} + +int32 ReadB (int32 va) +{ +int32 pa; + +pa = relocR (va); /* relocate */ +if (BPT_SUMM_RD && + (sim_brk_test (va & 0177777, BPT_RDVIR) || + sim_brk_test (pa, BPT_RDPHY))) /* read breakpoint? */ + ABORT (ABRT_BKPT); /* stop simulation */ +return PReadB (pa); +} + +/* Read word with breakpoint check: if a data breakpoint is encountered, + set reason accordingly but don't do an ABORT. This is used when we want + to break after doing the operation, used for interrupt processing. */ +int32 ReadCW (int32 va) +{ +int32 pa; + +if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); + } +pa = relocR (va); /* relocate */ +if (BPT_SUMM_RD && + (sim_brk_test (va & 0177777, BPT_RDVIR) || + sim_brk_test (pa, BPT_RDPHY))) /* read breakpoint? */ + reason = STOP_IBKPT; /* report that */ +return PReadW (pa); +} + +int32 ReadMW (int32 va) +{ +if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); + } +last_pa = relocW (va); /* reloc, wrt chk */ +if (BPT_SUMM_RW && + (sim_brk_test (va & 0177777, BPT_RWVIR) || + sim_brk_test (last_pa, BPT_RWPHY))) /* read or write breakpoint? */ + ABORT (ABRT_BKPT); /* stop simulation */ +return PReadW (last_pa); +} + +int32 ReadMB (int32 va) +{ +last_pa = relocW (va); /* reloc, wrt chk */ +if (BPT_SUMM_RW && + (sim_brk_test (va & 0177777, BPT_RWVIR) || + sim_brk_test (last_pa, BPT_RWPHY))) /* read or write breakpoint? */ + ABORT (ABRT_BKPT); /* stop simulation */ +return PReadB (last_pa); +} + +int32 PReadW (int32 pa) +{ +int32 data; + if (ADDR_IS_MEM (pa)) /* memory address? */ return (M[pa >> 1]); if (pa < IOPAGEBASE) { /* not I/O address? */ @@ -2555,13 +2700,12 @@ if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */ return data; } -int32 ReadB (int32 va) +int32 PReadB (int32 pa) { -int32 pa, data; +int32 data; -pa = relocR (va); /* relocate */ if (ADDR_IS_MEM (pa)) - return (va & 1? M[pa >> 1] >> 8: M[pa >> 1]) & 0377; + return (pa & 1? M[pa >> 1] >> 8: M[pa >> 1]) & 0377; if (pa < IOPAGEBASE) { /* not I/O address? */ setCPUERR (CPUE_NXM); ABORT (TRAP_NXM); @@ -2570,47 +2714,7 @@ if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */ setCPUERR (CPUE_TMO); ABORT (TRAP_NXM); } -return ((va & 1)? data >> 8: data) & 0377; -} - -int32 ReadMW (int32 va) -{ -int32 data; - -if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ - setCPUERR (CPUE_ODD); - ABORT (TRAP_ODD); - } -last_pa = relocW (va); /* reloc, wrt chk */ -if (ADDR_IS_MEM (last_pa)) /* memory address? */ - return (M[last_pa >> 1]); -if (last_pa < IOPAGEBASE) { /* not I/O address? */ - setCPUERR (CPUE_NXM); - ABORT (TRAP_NXM); - } -if (iopageR (&data, last_pa, READ) != SCPE_OK) { /* invalid I/O addr? */ - setCPUERR (CPUE_TMO); - ABORT (TRAP_NXM); - } -return data; -} - -int32 ReadMB (int32 va) -{ -int32 data; - -last_pa = relocW (va); /* reloc, wrt chk */ -if (ADDR_IS_MEM (last_pa)) - return (va & 1? M[last_pa >> 1] >> 8: M[last_pa >> 1]) & 0377; -if (last_pa < IOPAGEBASE) { /* not I/O address? */ - setCPUERR (CPUE_NXM); - ABORT (TRAP_NXM); - } -if (iopageR (&data, last_pa, READ) != SCPE_OK) { /* invalid I/O addr? */ - setCPUERR (CPUE_TMO); - ABORT (TRAP_NXM); - } -return ((va & 1)? data >> 8: data) & 0377; +return ((pa & 1)? data >> 8: data) & 0377; } /* Write byte and word routines @@ -2631,19 +2735,11 @@ if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ ABORT (TRAP_ODD); } pa = relocW (va); /* relocate */ -if (ADDR_IS_MEM (pa)) { /* memory address? */ - M[pa >> 1] = data; - return; - } -if (pa < IOPAGEBASE) { /* not I/O address? */ - setCPUERR (CPUE_NXM); - ABORT (TRAP_NXM); - } -if (iopageW (data, pa, WRITE) != SCPE_OK) { /* invalid I/O addr? */ - setCPUERR (CPUE_TMO); - ABORT (TRAP_NXM); - } -return; +if (BPT_SUMM_WR && + (sim_brk_test (va & 0177777, BPT_WRVIR) || + sim_brk_test (pa, BPT_WRPHY))) /* write breakpoint? */ + ABORT (ABRT_BKPT); /* stop simulation */ +PWriteW (data, pa); } void WriteB (int32 data, int32 va) @@ -2651,21 +2747,30 @@ void WriteB (int32 data, int32 va) int32 pa; pa = relocW (va); /* relocate */ -if (ADDR_IS_MEM (pa)) { /* memory address? */ - if (va & 1) - M[pa >> 1] = (M[pa >> 1] & 0377) | (data << 8); - else M[pa >> 1] = (M[pa >> 1] & ~0377) | data; - return; - } -if (pa < IOPAGEBASE) { /* not I/O address? */ - setCPUERR (CPUE_NXM); - ABORT (TRAP_NXM); +if (BPT_SUMM_WR && + (sim_brk_test (va & 0177777, BPT_WRVIR) || + sim_brk_test (pa, BPT_WRPHY))) /* write breakpoint? */ + ABORT (ABRT_BKPT); /* stop simulation */ +PWriteB (data, pa); +} + +/* Write word with breakpoint check: if a data breakpoint is encountered, + set reason accordingly but don't do an ABORT. This is used when we want + to break after doing the operation, used for interrupt processing. */ +void WriteCW (int32 data, int32 va) +{ +int32 pa; + +if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); } -if (iopageW (data, pa, WRITEB) != SCPE_OK) { /* invalid I/O addr? */ - setCPUERR (CPUE_TMO); - ABORT (TRAP_NXM); - } -return; +pa = relocW (va); /* relocate */ +if (BPT_SUMM_WR && + (sim_brk_test (va & 0177777, BPT_WRVIR) || + sim_brk_test (pa, BPT_WRPHY))) /* write breakpoint? */ + reason = STOP_IBKPT; /* report that */ +PWriteW (data, pa); } void PWriteW (int32 data, int32 pa) @@ -3243,7 +3348,10 @@ if (M == NULL) { /* First time init */ return SCPE_MEM; sim_set_pchar (0, "01000023640"); /* ESC, CR, LF, TAB, BS, BEL, ENQ */ sim_brk_dflt = SWMASK ('E'); - sim_brk_types = sim_brk_dflt | SWMASK ('P'); + sim_brk_types = sim_brk_dflt|SWMASK ('P')| + SWMASK ('R')|SWMASK ('S')| + SWMASK ('W')|SWMASK ('X'); + sim_brk_type_desc = cpu_breakpoints; sim_vm_is_subroutine_call = &cpu_is_pc_a_subroutine_call; auto_config(NULL, 0); /* do an initial auto configure */ } diff --git a/PDP11/pdp11_defs.h b/PDP11/pdp11_defs.h index 30d37d41..450c0b52 100644 --- a/PDP11/pdp11_defs.h +++ b/PDP11/pdp11_defs.h @@ -429,6 +429,7 @@ typedef struct { #define TRAP_V_PWRFL 13 /* power fail 24 */ #define TRAP_V_FPE 14 /* fpe 244 */ #define TRAP_V_MAX 15 /* intr = max trp # */ +#define ABRT_V_BKPT 16 /* stop due to breakpt */ #define TRAP_RED (1u << TRAP_V_RED) #define TRAP_ODD (1u << TRAP_V_ODD) #define TRAP_MME (1u << TRAP_V_MME) @@ -446,6 +447,7 @@ typedef struct { #define TRAP_FPE (1u << TRAP_V_FPE) #define TRAP_INT (1u << TRAP_V_MAX) #define TRAP_ALL ((1u << TRAP_V_MAX) - 1) /* all traps */ +#define ABRT_BKPT (1u << ABRT_V_BKPT) #define VEC_RED 0004 /* trap vectors */ #define VEC_ODD 0004 @@ -802,6 +804,26 @@ typedef struct pdp_dib DIB; #define SP R[6] #define PC R[7] +/* Codes for breakpoint support */ +#define BPT_PCVIR SWMASK('E') +#define BPT_PCPHY SWMASK('P') +#define BPT_RDVIR SWMASK('R') +#define BPT_RDPHY SWMASK('S') +#define BPT_WRVIR SWMASK('W') +#define BPT_WRPHY SWMASK('X') +/* We can test for two types of breakpoints in one sim_brk_test call, + which is useful when checking for read or write in the + read-modify-write routines. */ +#define BPT_RWVIR (BPT_RDVIR | BPT_WRVIR) +#define BPT_RWPHY (BPT_RDPHY | BPT_WRPHY) +/* Macros to check sim_brk_summ. We have enough different breakpoint + types that checking for the specific bits before doing sim_brk_test + is worth the extra few instructions. */ +#define BPT_SUMM_PC (sim_brk_summ & (BPT_PCVIR | BPT_PCPHY)) +#define BPT_SUMM_RD (sim_brk_summ & (BPT_RDVIR | BPT_RDPHY)) +#define BPT_SUMM_WR (sim_brk_summ & (BPT_WRVIR | BPT_WRPHY)) +#define BPT_SUMM_RW (sim_brk_summ & (BPT_RWVIR | BPT_RWPHY)) + /* Function prototypes */ int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf); diff --git a/PDP11/pdp11_fp.c b/PDP11/pdp11_fp.c index 7f279491..89aa1639 100644 --- a/PDP11/pdp11_fp.c +++ b/PDP11/pdp11_fp.c @@ -227,6 +227,7 @@ extern int32 R[8]; extern int32 STKLIM; extern int32 cm, isenable, dsenable, MMR0, MMR1; extern fpac_t FR[6]; +extern int32 last_pa; fpac_t zero_fac = { 0, 0 }; fpac_t one_fac = { 1, 0 }; @@ -263,8 +264,11 @@ void frac_mulfp11 (fpac_t *src1, fpac_t *src2); int32 roundfp11 (fpac_t *src); int32 round_and_pack (fpac_t *fac, int32 exp, fpac_t *frac, int r); +extern int32 relocW (int32 addr); extern int32 ReadW (int32 addr); +extern int32 ReadMW (int32 addr); extern void WriteW (int32 data, int32 addr); +extern void PWriteW (int32 data, int32 addr); extern void set_stack_trap (int32 adr); /* Set up for instruction decode and execution */ @@ -800,10 +804,29 @@ return TRUE; void WriteI (int32 data, int32 VA, int32 spec, int32 len) { -WriteW ((data >> 16) & 0177777, VA); -if ((len == WORD) || (spec == 027)) +int32 pa, pa2; + +if ((len == WORD) || (spec == 027)) { + WriteW ((data >> 16) & 0177777, VA); return; -WriteW (data & 0177777, (VA & ~0177777) | ((VA + 2) & 0177777)); + } + +/* Check both word addresses for breakpoints, and only then + do the writes. */ +if ((VA & 1) && CPUT (HAS_ODD)) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); + } +pa = relocW (VA); /* relocate */ +pa2 = relocW ((VA & ~0177777) | ((VA + 2) & 0177777)); +if (BPT_SUMM_WR && + (sim_brk_test (VA & 0177777, BPT_WRVIR) || + sim_brk_test (pa, BPT_WRPHY) || + sim_brk_test ((VA + 2) & 0177777, BPT_WRVIR) || + sim_brk_test (pa2, BPT_WRPHY))) /* write breakpoint? */ + ABORT (ABRT_BKPT); /* stop simulation */ +PWriteW ((data >> 16) & 0177777, pa); +PWriteW (data & 0177777, pa2); return; } @@ -819,21 +842,55 @@ return; void WriteFP (fpac_t *fptr, int32 VA, int32 spec, int32 len) { -int32 exta; +int32 exta, pa, pa2, pa3, pa4; if (spec <= 07) { F_STORE_P (len == QUAD, fptr, FR[spec]); return; } -WriteW ((fptr->h >> FP_V_F0) & 0177777, VA); -if (spec == 027) +if (spec == 027) { + WriteW ((fptr->h >> FP_V_F0) & 0177777, VA); return; + } + +/* Check all word addresses for breakpoints, and only then + do the writes. */ +if ((VA & 1) && CPUT (HAS_ODD)) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); + } exta = VA & ~0177777; -WriteW ((fptr->h >> FP_V_F1) & 0177777, exta | ((VA + 2) & 0177777)); +pa = relocW (VA); /* relocate */ +pa2 = relocW (exta | ((VA + 2) & 0177777)); +if (len == LONG) { + if (BPT_SUMM_WR && + (sim_brk_test (VA & 0177777, BPT_WRVIR) || + sim_brk_test (pa, BPT_WRPHY) || + sim_brk_test ((VA + 2) & 0177777, BPT_WRVIR) || + sim_brk_test (pa2, BPT_WRPHY))) /* write breakpoint? */ + ABORT (ABRT_BKPT); /* stop simulation */ + } +else { + pa3 = relocW (exta | ((VA + 4) & 0177777)); + pa4 = relocW (exta | ((VA + 6) & 0177777)); + if (BPT_SUMM_WR && + (sim_brk_test (VA & 0177777, BPT_WRVIR) || + sim_brk_test (pa, BPT_WRPHY) || + sim_brk_test ((VA + 2) & 0177777, BPT_WRVIR) || + sim_brk_test (pa2, BPT_WRPHY) || + sim_brk_test ((VA + 4) & 0177777, BPT_WRVIR) || + sim_brk_test (pa3, BPT_WRPHY) || + sim_brk_test ((VA + 6) & 0177777, BPT_WRVIR) || + sim_brk_test (pa4, BPT_WRPHY))) /* write breakpoint? */ + ABORT (ABRT_BKPT); /* stop simulation */ + } + +PWriteW ((fptr->h >> FP_V_F0) & 0177777, pa); +PWriteW ((fptr->h >> FP_V_F1) & 0177777, pa2); if (len == LONG) return; -WriteW ((fptr->l >> FP_V_F2) & 0177777, exta | ((VA + 4) & 0177777)); -WriteW ((fptr->l >> FP_V_F3) & 0177777, exta | ((VA + 6) & 0177777)); +PWriteW ((fptr->l >> FP_V_F2) & 0177777, pa3); +PWriteW ((fptr->l >> FP_V_F3) & 0177777, pa4); return; } @@ -841,7 +898,7 @@ return; t_stat fis11 (int32 IR) { -int32 reg, exta; + int32 reg, exta, pa, pa2; fpac_t fac, fsrc; reg = IR & 07; /* isolate reg */ @@ -859,8 +916,13 @@ FPS = FPS_IU|FPS_IV; /* trap ovf,unf */ fsrc.h = (ReadW (exta | R[reg]) << FP_V_F0) | (ReadW (exta | ((R[reg] + 2) & 0177777)) << FP_V_F1); fsrc.l = 0; -fac.h = (ReadW (exta | ((R[reg] + 4) & 0177777)) << FP_V_F0) | - (ReadW (exta | ((R[reg] + 6) & 0177777)) << FP_V_F1); +/* Note that we do ReadMW here, because those two memory locations will + be written later with the result. This also ensures we'll take write + breakpoints at this stage (before writing anything) if applicable. */ +fac.h = ReadMW (exta | ((R[reg] + 4) & 0177777)) << FP_V_F0; +pa = last_pa; +fac.h |= ReadMW (exta | ((R[reg] + 6) & 0177777)) << FP_V_F1; +pa2 = last_pa; fac.l = 0; if (GET_SIGN (fsrc.h) && (GET_EXP (fsrc.h) == 0)) /* clean 0's */ fsrc.h = fsrc.l = 0; @@ -895,8 +957,8 @@ switch ((IR >> 3) & 3) { /* case IR<5:3> */ } if (FEC == 0) { /* no err? */ - WriteW ((fac.h >> FP_V_F0) & 0177777, exta | ((R[reg] + 4) & 0177777)); - WriteW ((fac.h >> FP_V_F1) & 0177777, exta | ((R[reg] + 6) & 0177777)); + PWriteW ((fac.h >> FP_V_F0) & 0177777, pa); + PWriteW ((fac.h >> FP_V_F1) & 0177777, pa2); R[reg] = (R[reg] + 4) & 0177777; /* pop stack */ N = (GET_SIGN (fac.h) != 0); /* set N,Z */ Z = (fac.h == 0); diff --git a/doc/pdp11_doc.doc b/doc/pdp11_doc.doc index 9b1c29ee803ea27e2bafe257db0f278510606b8f..985ad636efde1d4c0fb8b58b51eae674269440dd 100644 GIT binary patch delta 22730 zcma*v1wa&O|G@EQhLRF&RM52xJW;GuEbIUj^Gt+AQDBj zTd~_yu|4JgeRf$z@80+QuYUHKotd3^=6Qb4GszBH={W3?3d2JzM_b1LL6+Vi0q)b zsC8yI{2$9n= zk@ODBkwFs37FHmYl`UUOnQtiR-PWd$UrS7WrurR)B?G1{sAVHRmXuZEY}w>yj7gMv zEo#}iA5%+hln9^@N~)$Ml`Qb%T$azn$-j6z}5dd}XRbK3nffUP1ck4IU2t>l&R~_tfkB0^8_f$fFU|m#vWOsdx&Jd;x4G#;`$LOQtblqe0!M±S9zNh}OrJ*XiUpL-k?7@ey&l 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