; $Id: cpu_mmu.mac 1290 2022-08-30 06:20:40Z mueller $ ; SPDX-License-Identifier: GPL-3.0-or-later ; Copyright 2022- by Walter F.J. Mueller ; ; Revision History: ; Date Rev Version Comment ; 2022-08-06 1272 1.0.1 ssr->mmr rename ; 2022-07-28 1264 1.0 Initial version ; 2022-07-24 1262 0.1 First draft ; ; Test CPU MMU: all aspects of the MMU ; Section A: pdr,par registers ; Section B: mmr0,mmr3 registers, mapping, instructions ; Section C: mmr1+mmr0 register, aborts and traps ; Section D: mmr2+mmr1+mmr0 register, abort recovery ; .include |lib/tcode_std_base.mac| .include |lib/defs_mmu.mac| ; some useful definitions uipdr0 = uipdr+ 0 uipar0 = uipar+ 0 udpdr0 = udpdr+ 0 udpar0 = udpar+ 0 udpdr1 = udpdr+ 2 udpar1 = udpar+ 2 udpdr2 = udpdr+ 4 udpar2 = udpar+ 4 sipdr0 = sipdr+ 0 sipar0 = sipar+ 0 sipdr7 = sipdr+16 sipar7 = sipar+16 kipdr0 = kipdr+ 0 kipdr6 = kipdr+14 kipar6 = kipar+14 kipdr7 = kipdr+16 kipar7 = kipar+16 ; ; Section A: pdr,par registers =============================================== ; ; Test A1: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; ; Test A1.1 -- test that pdr/par are 16 bit write/readable +++++++++++ ; Write unique patterns to first and last pdr/par of each mode and read back ; ta0101: mov #000401,r5 ; pattern master ;;; mov #077717,r5 ; bit mask for pdr ; ; write 000401,001002,002004,004010,... for pdr; complement for par mov r5,r0 ; start pattern mov #12.,r1 ; number tested regs mov #9000$,r2 1000$: mov (r2)+,r3 mov r0,(r3) ; write pdr add r5,r0 dec r0 mov r0,40(r3) ; write par add r5,r0 sob r1,1000$ ; ; read back mov r5,r0 ; start pattern mov #12.,r1 ; number of modes mov #9000$,r2 1100$: mov (r2)+,r3 hcmpeq (r3),r0 ; check pdr add r5,r0 dec r0 hcmpeq 40(r3),r0 ; check par add r5,r0 sob r1,1100$ ; ; complement all pattern mov #12.,r1 ; number of modes mov #9000$,r2 1200$: mov (r2)+,r3 mov (r3),r4 ; complement pdr only writable bits com r4 bic #100360,r4 ; mask non-writable (incl A and W) mov r4,(r3) com 40(r3) ; complement par sob r1,1200$ ; ; and read back again ; pdr only plf,ed and acf fields are checked ; par all 18 bits are write/readable mov r5,r0 ; start pattern com r0 ; complemented mov #12.,r1 ; number of modes mov #9000$,r2 1300$: mov (r2)+,r3 mov r0,r4 bic #100360,r4 ; mask non-writable (incl A and W) hcmpeq (r3),r4 ; check pdr only writable bits sub r5,r0 inc r0 hcmpeq 40(r3),r0 ; check par sub r5,r0 sob r1,1300$ ; jmp 9999$ ; 9000$: .word uipdr ; usr i page dsc base 0 .word uipdr+16 .word udpdr ; usr d page dsc base 0 .word udpdr+16 .word sipdr ; sup i page dsc base 0 .word sipdr+16 .word sdpdr ; sup d page dsc base 0 .word sdpdr+16 .word kipdr ; ker i page dsc base 0 .word kipdr+16 .word kdpdr ; ker d page dsc base 0 .word kdpdr+16 ; 9999$: iot ; end of test A1.1 ; ; Test A1.2 -- set up MMU default configuration ++++++++++++++++++++++ ; Nothing is verified, just sets the MMU for all further tests ; kernel I: 1-to-1 and seg7 to io-page ; kernel D: unmapped but seg7 to io-page ; supervisor and user I and D: unmapped (acf=0) ; ta0102: ; first clear all pdr/par, that disables mapping (acf=0) mov #1000$,r0 mov #3,r1 100$: mov (r0)+,r2 ; ptr to pdr+par I+D set (32 regs) mov #8.,r3 ; 8 chunks of 4 200$: clr (r2)+ clr (r2)+ clr (r2)+ clr (r2)+ sob r3,200$ sob r1,100$ ; set up kernel I mov #kipdr,r0 mov #<127.*md.plf>!md.arw,r1 ; plf=127; ed=0(up); acf=6(w/r) mov r1,(r0)+ ; kipdr0 mov r1,(r0)+ ; kipdr1 mov r1,(r0)+ ; kipdr2 mov r1,(r0)+ ; kipdr3 mov r1,(r0)+ ; kipdr4 mov r1,(r0)+ ; kipdr5 mov r1,(r0)+ ; kipdr6 mov r1,(r0)+ ; kipdr7 mov #kipar,r0 mov #000000,(r0)+ ; kipar0 000000 base mov #000200,(r0)+ ; kipar1 020000 base mov #000400,(r0)+ ; kipar2 040000 base mov #000600,(r0)+ ; kipar3 060000 base mov #001000,(r0)+ ; kipar4 100000 base mov #001200,(r0)+ ; kipar5 120000 base mov #001400,(r0)+ ; kipar6 140000 base mov #177600,(r0)+ ; kipar7 (map I/O page) ; kernel D space is not used in tests, kernel always runs without I/D ; D space is tested, but in supervisor or user mode ; jmp 9999$ ; 1000$: .word uipdr .word sipdr .word kipdr ; 9999$: iot ; end of test A1.2 ; ; Section B: mmr0,mmr3 registers, mapping, instructions ====================== ; Test whether address mapping works (traps and aborts avoided) ; ; Test B1: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Test mmr0, mmr3 write/read and clear by RESET ; ; This test verifies ; x xxx xxx xxx xxx xxx NZVC Instruction / Remark ; 0 000 000 000 000 101 ---- RESET (clear mmr0,mmr3) ; ; Test B1.1 -- test mmr0 write/read ++++++++++++++++++++++++++++++++++ ; Test all writable bits except m0.ena ; tb0101: mov #mmr0,r0 ; ptr to mmr0 mov #m0.ico,r1 ; instruction complete flag mov #1010$,r4 ; ptr to data mov #1011$,r3 ; ptr to data end 100$: mov (r4),(r0) ; write mmr0 mov (r0),r5 ; read mmr0 bic r1,r5 ; mask instruction complete hcmpeq r5,(r4)+ ; check cmp r4,r3 ; more to do ? blo 100$ ; reset ; mmr0 has 5 bits set, check clear mov (r0),r5 ; read mmr0 bic r1,r5 ; mask instruction complete htsteq r5 ; check mmr0 cleared jmp 9999$ ; 1010$: .word m0.anr ; abort flags .word m0.ale .word m0.ard .word m0.trp ; trap flag .word m0.ent ; trap enable .word m0.anr!m0.ale!m0.ard!m0.trp!m0.ent 1011$: ; 9999$: iot ; end of test B1.1 ; ; Test B1.2 -- test mmr3 write/read ++++++++++++++++++++++++++++++++++ ; Test all writable bits; mmu is off, and unibus map not used ; tb0102: mov #mmr3,r0 ; ptr to mmr3 mov #1010$,r4 ; ptr to data mov #1011$,r3 ; ptr to data end 100$: mov (r4),(r0) ; write mmr3 hcmpeq (r0),(r4)+ ; check cmp r4,r3 ; more to do ? blo 100$ ; reset ; mmr3 has 5 bits set, check clear htsteq (r0) ; check mmr3 cleared jmp 9999$ ; 1010$: .word m3.eub .word m3.e22 .word m3.dkm .word m3.dsm .word m3.dum .word m3.eub!m3.e22!m3.dkm!m3.dsm!m3.dum 1011$: ; 9999$: iot ; end of test B1.2 ; ; Test B2: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Test kernel mode mapping ; ; Test B2.1 -- test 1-to-1 kernel mode mapping +++++++++++++++++++++++ ; simply enable MMU, shouldnt make a difference ; test that 18bit mode extends I/O page addressing ; test that RESET clears mmr0 and mmr3 ; tb0201: mov #123456,1000$ ; enable mmu in 18bit mode clr mmr3 ; no d dspace, no 22bit mov #m0.ena,mmr0 ; enable mmu hbitne #m0.ena,mmr0 ; test bit ;! MMU 18 hcmpeq 1000$,#123456 ; check marker ; verify I/O page mapping in 18bit mode (007600 must be OK) mov #kipar7,r0 ; ptr to kipar7 bic #170000,(r0) ; clear to 4 bits in kipar7 hcmpeq (r0),#007600 ; kipar7 still seen ??? bis #170000,(r0) ; restore kipar7 hcmpeq (r0),#177600 ; enable mmu in 22bit mode; check that mmr3 still seen mov #m3.e22,mmr3 hcmpeq mmr3,#m3.e22 ; test mmr3 stll seen ??? ;! MMU 22 ; test RESET reset ; should clear mmr0 and mmr3 htsteq mmr0 ; check mmr0 cleared ;! MMU off htsteq mmr3 ; check mmr3 cleared jmp 9999$ ; 1000$: .word 0 ; 9999$: iot ; end of test B2.1 ; ; Test B2.2 -- test variable kernel mode mapping +++++++++++++++++++++ ; change seg6 mapping ; test that 18bit mode discards the 4 MSB of the par ; tb0202: mov #kipar6,r0 ; ptr to kipar6 mov #140000,r5 ; seg6 base mov #140000,(r5) ; init markers clr 2(r5) mov #140100,100(r5) ; init markers clr 102(r5) ; clr mmr3 ; no d dspace, no 22bit mov #m0.ena,mmr0 ; enable mmu ;! MMU 18 ; check in 1-to-1 mapping hcmpeq (r5),#140000 htsteq 2(r5) hcmpeq 100(r5),#140100 htsteq 102(r5) ; move seg6 mapping up by one click inc (r0) ; move kipar6 base up hcmpeq (r0),#001401 hcmpeq 0(r5),#140100 mov #010000,2(r5) ; write marker ; set MSBs in kipar6, should be discarded in 18bit mode, check markers bis #170000,(r0) hcmpeq (r0),#171401 hcmpeq (r5),#140100 ; check marker bic #170000,(r0) hcmpeq (r0),#001401 ; move seg6 mapping down by one click dec (r0) ; move kipar6 base up hcmpeq (r0),#001400 mov #020000,2(r5) ; write marker hcmpeq 2(r5),#020000 ; check marker hcmpeq 102(r5),#010000 ; check marker ; disable MMU reset ; mmu off ;! MMU off ; 9999$: iot ; end of test B2.2 ; ; Test B3: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Test user and supervisor mode ; ; Test B3.1 -- run code in user/supervisor mode ++++++++++++++++++++++ ; code vc0 is executed in user and in supervisor mode ; the code runs in seg0 with D space disabled ; tb0301: ; set up emt handler mov #vhuemt,v..emt clr v..emt+2 ; pr0 kernel ; enable mmu clr mmr3 ; no d dspace, no 22bit mov #m0.ena,mmr0 ; enable mmu ;! MMU 18 ; ; run code vc0 in user mode -------------------------------- ; ; set user mode pdr/par, only short page 0 mov #<8.*md.plf>!md.arw,uipdr0 mov #,uipar0 ; set up data for user mode run mov #023456,vc0v0; mov #000123,vc0v1 mov #077321,vc0v2 ; start code in user mode mov #1000$,vhustp ; set up continuation address mov #,-(sp) ; next psw: user mode clr -(sp) ; will start at 0 rti ; and launch it halt 1000$: ; continuation point ; check psw ccc ; clear cc -> psw reflects pm setting hcmpeq cp.psw,#cp.pmu ; check pm ; check data hcmpeq vc0v0,#154321 hcmpeq vc0v2,#077444 ; reset user mode pdr/par clr uipdr0 clr uipar0 ; ; run code vc0 in supervisor mode -------------------------- ; ; set supervisor mode pdr/par, only short page 0 mov #<8.*md.plf>!md.arw,sipdr0 mov #,sipar0 ; set up data for user mode run mov #017171,vc0v0 mov #000321,vc0v1 mov #100123,vc0v2 ; start code in supervisor mode mov #2000$,vhustp ; set up continuation address mov #,-(sp) ; next psw: supervisor mode clr -(sp) ; will start at 0 rti ; and launch it halt 2000$: ; continuation point ; check psw ccc ; clear cc -> psw reflects pm setting hcmpeq cp.psw,#cp.pms ; check pm ; check data hcmpeq vc0v0,#160606 hcmpeq vc0v2,#100444 ; reset supervsior mode pdr/par clr sipdr0 clr sipar0 ; reset ; mmu off ;! MMU off mov #v..emt+2,v..emt ; restore emt catcher clr v..emt+2 ; 9999$: iot ; end of test B3.1 ; ; Test B3.2 -- run code in user mode with D space enabled ++++++++++++ ; code vc1 is executed in user and in supervisor mode ; the code runs in seg0 with D space enabled ; ; This test verifies ; x xxx xxx xxx xxx xxx NZVC Instruction / Remark ; 0 000 110 101 ddd ddd NZ0- MFPI ; 0 000 110 110 ddd ddd NZ0- MTPI ; 1 000 110 101 ddd ddd NZ0- MFPD ; 1 000 110 110 ddd ddd NZ0- MTPD ; tb0302: ; set up emt handler mov #vhuemt,v..emt clr v..emt+2 ; pr0 kernel ; enable mmu mov #m3.dum,mmr3 ; user d dspace, no 22bit mov #m0.ena,mmr0 ; enable mmu ;! MMU 18 ; ; run code vc1 in user mode -------------------------------- ; ; set user mode pdr/par, only short page 0; I and D mov #<8.*md.plf>!md.arw,uipdr0 mov #,uipar0 mov #<8.*md.plf>!md.arw,udpdr0 mov #,udpar0 ; set up data for user mode run mov #020305,vc1v0 mov #000212,vc1v1 mov #033121,vc1v2 ; start code in user mode mov #1000$,vhustp ; set up continuation address mov #,-(sp) ; next psw: user mode clr -(sp) ; will start at 0 rti ; and launch it halt 1000$: ; continuation point ; check psw ccc ; clear cc -> psw reflects pm setting hcmpeq cp.psw,#cp.pmu ; check pm ; check data hcmpeq vc1v0,#157472 hcmpeq vc1v2,#033333 ; ; psw has now pm=user and cm=kernel; good setup to test MFPI and friends ; ; test MFPD (data access) ; mov #,r5 ; initialize data pointer mfpd (r5) ; read vc1v0 hcmpeq (sp)+,#157472 mfpd @# ; read vc1v2 hcmpeq (sp)+,#033333 ; ; test MTPD and MFPD, incl cc (data access) ; mov #2010$,r4 ; ptr to test data mov #2011$,r3 ; ptr to test end 2000$: push (r4)+ ccc ; C=0 mtpd (r5) ; write vc1v0 hcmpeq cp.psw,(r4)+ ; check cc hcmpeq vc1v0,-4(r4) ; check target scc ; C=1 mfpd (r5) hcmpeq cp.psw,(r4)+ ; check cc hcmpeq (sp)+,-6(r4) ; check data cmp r4,r3 ; more to do ? blo 2000$ ; ; test MFPI (data access) ; mov #,r5 ; initialize data pointer mfpi (r5) ; read 1st instruction word hcmpeq (sp)+,vc1 ; ; test MTPI and MFPI, incl cc (data access) ; mov #,r5 ; initialize data pointer mov #3010$,r4 ; ptr to test data mov #3011$,r3 ; ptr to test end 3000$: push (r4)+ scc ; C=1 mtpi (r5) ; write vc1ida hcmpeq cp.psw,(r4)+ ; check cc hcmpeq vc1ida,-4(r4) ; check target ccc ; C=0 mfpi (r5) hcmpeq cp.psw,(r4)+ ; check cc hcmpeq (sp)+,-6(r4) ; check data cmp r4,r3 ; more to do ? blo 3000$ ; ; Test MTPD,MFPD with @(sp)+ ; Note: (sp)+ is not a useful address mode for MTPD ; It will use the cm sp as address in pm. ; So @(sp)+ is the only mode with sp in src worth to be tested ; clr vc1v0 push # ; D addr of vc1v0 push #054321 ; data to mtpd mtpd @(sp)+ ; reads data first, then dst addr hcmpeq vc1v0,#054321 ; check at destination inc vc1v0 push # ; D addr of vc1v0 mfpd @(sp)+ hcmpeq (sp)+,#054322 ; check ; ; Test MTPI,MFPI with @(sp)+ ; clr vc1ida push # ; I addr of vc1ida push #012321 ; data to mtpi mtpi @(sp)+ ; reads data first, then dst addr hcmpeq vc1ida,#012321 ; check at destination inc vc1ida push # ; I addr of vc1ida mfpi @(sp)+ hcmpeq (sp)+,#012322 ; check ; ; Test MFPD,MFPI and MTPD,MTPI for sp register access ; accessing sp will access user mode stack pointer (which is != kernel stack) ; ; read sp via mfpd and mfpi mfpd sp ; read user mode sp hcmbeq (sp)+,# ; check mfpi sp ; read user mode sp (same for I and D) hcmbeq (sp)+,# ; check ; write sp via mtpd, readback via mfpi mov sp,r4 ; remember kernel stack mov #77,r5 push r5 mtpd sp ; change user stack hcmpeq sp,r4 ; check kernel stack unchanged mfpi sp ; read back user stack hcmpeq (sp)+,r5 ; check ; write sp via mtpi, readback via mfpd mov #177,r5 push r5 mtpi sp ; change user stack hcmpeq sp,r4 ; check kernel stack unchanged mfpd sp ; read back user stack hcmpeq (sp)+,r5 ; check ; ; Test MFPD,MFPI and MTPD,MTPI for register r0-r5 access ; accessing r0-r5 simply acccesses common registers ; that is usually not used, but should work ; ; write registers via mtpd,mtpi push #277 mtpd r5 ; effective mov #277,r5 push #377 mtpi r4 ; effective mov #377,r4 hcmpeq r5,#277 ; check hcmpeq r4,#377 ; check ; read registers via mtpd,mtpi mov #477,r5 mov #577,r4 mfpd r5 hcmpeq (sp)+,#477 ; check mfpd r4 hcmpeq (sp)+,#577 ; check ; ; reset user mode pdr/par clr uipdr0 clr uipar0 clr udpdr0 clr udpar0 ; reset ; mmu off ;! MMU off clr cp.psw ; crop pm in psw mov #v..emt+2,v..emt ; restore emt catcher clr v..emt+2 jmp 9999$ ; ; test data for m*pd tests (C=0 for T and C=1 for F) 2010$: .word 000000,cp.pmu!cp0z00,cp.pmu!cp0z0c .word 000001,cp.pmu!cp0000,cp.pmu!cp000c .word 100000,cp.pmu!cpn000,cp.pmu!cpn00c 2011$: ; ; test data for m*pi tests (C=1 for T and C=0 for F) 3010$: .word 000000,cp.pmu!cp0z0c,cp.pmu!cp0z00 .word 000001,cp.pmu!cp000c,cp.pmu!cp0000 .word 100000,cp.pmu!cpn00c,cp.pmu!cpn000 3011$: ; 9999$: iot ; end of test B3.2 ; ; Section C: mmr1+mmr0 register, aborts and traps ============================ ; ; Test C1: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; ; Test C1.1 -- test mmr1 response via set abort in mmr0 trick ++++++++ ; Test method (concept seen in ekbee1) ; - start with mmr1 cleared, mmr1 will track ; - write one of the 3 abort bits in mmr1 (all three are tested) ; - that will freeze mmr1 ; - the register signature of the write can be inspected ; Note: this gives MMR1 when instruction is not(!) aborted ; in principle, MMR1 might hold other values in the case of an abort ; tc0101: mov #1000$,r1 ; ptr to abort bit table mov #mmr0,r2 ; ptr to mmr0 mov #mmr1,r3 ; ptr to mmr3 ; reset mov (r1),(r2) ; no regs changed ! hcmpeq (r3),#^b0000000000000000; ; reset mov (r1)+,(r2) ; r1,2 00000 000 00010 001 via anr hcmpeq (r3),#^b0000000000010001; ; reset mov -(r1),(r2) ; r1,-2 00000 000 11110 001 via anr hcmpeq (r3),#^b0000000011110001; ; reset mov (r1),(r2)+ ; r2,2 00000 000 00010 010 via anr hcmpeq (r3),#^b0000000000010010; ; reset mov (r1),-(r2) ; r2,-2 00000 000 11110 010 via anr hcmpeq (r3),#^b0000000011110010; ; reset mov (r1)+,(r2)+ ; r1,2,r2,2 00010 010 00010 001 via anr hcmpeq (r3),#^b0001001000010001; ; reset mov -(r1),-(r2) ; r1,-2,r2,-2 11110 010 11110 001 via anr hcmpeq (r3),#^b1111001011110001; ; reset tst (r1)+ ; bump ptr to ale mov (r1)+,(r2)+ ; r1,2,r2,2 00010 010 00010 001 via ale hcmpeq (r3),#^b0001001000010001; ; ; check that index reads are not accounted in mmr1 reset tst (r1)+ ; bump ptr beyond ard mov -(r1),-2(r2) ; r1,-1 00000 000 11110 001 via ard hcmpeq (r3),#^b0000000011110001; ; ; check @(pc)+ behavior ; w11 updates mmr1 in this case, as is also expected in ekbee1 ; Simh only adds 'general purpose register updates', thus not pc ; e11 doesnt like this test either tstb systyp ; skip test if on SimH or e11 blt 100$ ; systyp<0 --> not on w11 reset mov -(r1),@#mmr0 ; r1,-2,pc,2 00010 111 11110 001 via ale hcmpeq (r3),#^b0001011111110001; 100$: ; reset jmp 9999$ ; 1000$: .word m0.anr .word m0.ale .word m0.ard ; 9999$: iot ; end of test C1.1 ; ; Test C2: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; Verify MMU abort response in mmr0 and mmr1 ; ; Test C2.1 -- test unary/binary instructions ++++++++++++++++++++++++ ; Excercise access to kernel page 6 and inspect mmr0 and mmr1 ; tc0201: mov #vhemmu,v..mmu clr v..mmu+2 ; pr0 kernel reset mov #m0.ena,mmr0 ; enable mmu ;! MMU 18 ; ; part 1: unary instructions; test acf to mmr0(15:13) mapping -------- ; Summary: ; 1000$: tst (r2)+ ; r ; dst anr 1 ; pdr= 0.,0,000 ; 1100$: tst (r3)+ ; r ; dst ale 1 ; pdr= 0.,0,arw ; 1200$: tst -(r4) ; r ; dst ale 1 ; pdr=127.,1,arw ; 1300$: clrb -(r2) ; w ; dst ard 1 ; pdr= 0.,0,aro ; 1400$: inc (r2)+ ; m ; dst ard 1 ; pdr= 0.,0,aro ; 1500$: tstb (r2)+ ; r ; dst anr 1 ; pdr= 0.,0,011 ; 1600$: tstb -(r2) ; r ; dst anr 1 ; pdr= 0.,0,111 ; ; non-resident abort (only) 1000$: clr kipdr6 ; plf= 0.;ed=0;acf=nres mov #1010$,vhvmmu mov #140000,r2 tst (r2)+ ; will fail halt 1010$: .word m0.anr!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000000010010 ; mmr1 +2,2 ; ; length abort, up direction; length 1 click 1100$: mov #<0.*md.plf>!md.arw,kipdr6 ; plf= 0.;ed=0;acf=w/r mov #1110$,vhvmmu mov #140102,r3 tst (r3)+ ; will fail halt 1110$: .word m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000000010011 ; mmr1 +2,3 ; ; length abort, down direction; length 1 click 1200$: mov #<127.*md.plf>!md.dwn!md.arw,kipdr6 ; plf=127.;ed=1;acf=w/r mov #1210$,vhvmmu mov #157700,r4 tst -(r4) ; will fail halt 1210$: .word m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000011110100 ; mmr1 -2,4 ; ; write abort in mapped area (write access) 1300$: mov #<0.*md.plf>!md.aro,kipdr6 ; plf= 0.;ed=0;acf=r mov #140002,r2 tstb (r2)+ ; read ok mov #1310$,vhvmmu clrb -(r2) ; write will fail halt 1310$: .word m0.ard!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000011111010 ; mmr1 -1,2 ; ; write abort in mapped area (wmw access) 1400$: mov #1410$,vhvmmu inc (r2)+ ; rmw will fail halt 1410$: .word m0.ard!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000000010010 ; mmr1 +2,2 ; ; acf=011: reserved, abort all -> handled as non-resident 1500$: mov #^b011,kipdr6 ; plf= 0.;ed=0;acf=011 mov #1510$,vhvmmu tstb (r2)+ ; will fail halt 1510$: .word m0.anr!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000000001010 ; mmr1 +1,2 ; ; acf=111: reserved, abort all -> handled as non-resident 1600$: mov #^b111,kipdr6 ; plf= 0.;ed=0;acf=111 mov #1610$,vhvmmu tstb -(r2) ; will fail halt 1610$: .word m0.anr!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000011111010 ; mmr1 -1,2 ; ; part 2: unary instructions; fail in second access ------------------ ; Summary: ; 2000$: tst @(r2)+ ; r ; dst ale 2 ; pdr= 0.,0,arw ; 2100$: tst @-(r2) ; r ; dst ale 2 ; pdr= 0.,0,arw ; 2000$: mov #<0.*md.plf>!md.arw,kipdr6 ; plf= 0.;ed=0;acf=r/w mov #2010$,vhvmmu mov #2001$,r2 tst @(r2)+ ; will fail halt 2001$: .word 140102 ; probed address 2010$: .word m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000000010010 ; mmr1 +2,2 ; 2100$: mov #2110$,vhvmmu tst @-(r2) ; will fail halt 2110$: .word m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000011110010 ; mmr1 -2,2 ; ; part 3: binary instructions; fail in src field --------------------- ; Summary: ; 3000$: mov (r2)+,(r3)+ ; r w ; src ale 1 ; pdr= 0.,0,arw ; 3100$: mov -(r2),(r3)+ ; r w ; src ale 1 ; pdr= 0.,0,arw ; 3200$: mov @(r4)+,(r3)+ ; r w ; src ale 2 ; pdr= 0.,0,arw ; 3300$: mov @-(r4),(r3)+ ; r w ; src ale 2 ; pdr= 0.,0,arw ; 3000$: mov #<0.*md.plf>!md.arw,kipdr6 ; plf= 0.;ed=0;acf=r/w mov #3010$,vhvmmu mov #140102,r2 mov #1,r3 ; not used mov (r2)+,(r3)+ ; will fail halt 3010$: .word m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000000010010 ; mmr1 +2,2 ; 3100$: mov #3110$,vhvmmu mov -(r2),(r3)+ ; will fail halt 3110$: .word m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000011110010 ; mmr1 -2,2 ; 3200$: mov #3210$,vhvmmu mov #3201$,r4 mov @(r4)+,(r3)+ ; will fail halt 3201$: .word 140102 ; probed address 3210$: .word m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000000010100 ; mmr1 +2,4 ; 3300$: mov #3310$,vhvmmu movb @-(r4),(r3)+ ; will fail halt 3310$: .word m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000011110100 ; mmr1 -2,4 ; ; part 4: binary instructions; fail in dst field --------------------- ; Summary: ; 4000$: mov (r2)+,-(r3) ; r w ; dst ale 1 ; pdr= 0.,0,arw ; 4100$: mov -(r2),(r3)+ ; r w ; dst ale 1 ; pdr= 0.,0,arw ; 4200$: cmp (r2)+,@(r5)+ ; r r ; dst ale 2 ; pdr= 0.,0,arw ; 4300$: bis -(r2),(r4)+ ; r m ; dst ard 1 ; pdr= 0.,0,aro ; 4400$: bis (r2),-(r4) ; r m ; dst ard 1 ; pdr= 0.,0,aro ; 4000$: mov #<0.*md.plf>!md.arw,kipdr6 ; plf= 0.;ed=0;acf=r/w mov #4010$,vhvmmu mov #4001$,r2 mov #140104,r3 mov (r2)+,-(r3) ; will fail halt 4001$: .word 123456 4010$: .word m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b1111001100010010 ; mmr1 -2,3; +2,2 ; 4100$: mov #4110$,vhvmmu mov -(r2),(r3)+ ; will fail halt 4110$: .word m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0001001111110010 ; mmr1 +2,3; -2,2 ; 4200$: mov #4210$,vhvmmu mov 4201$,r5 cmp (r2)+,@(r5)+ ; will fail halt 4201$: .word 140102 ; probed address 4210$: .word m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0001010100010010 ; mmr1 +2,5; +2,2 ; 4300$: mov #<0.*md.plf>!md.aro,kipdr6 ; plf= 0.;ed=0;acf=r mov #4310$,vhvmmu mov #140010,r4 bis -(r2),(r4)+ ; will fail halt 4310$: .word m0.ard!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0001010011110010 ; mmr1 +2,4; -2,2 ; ; no inc/dec for src 4400$: mov #4410$,vhvmmu bis (r2),-(r4) ; will fail halt 4410$: .word m0.ard!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000011110100 ; mmr1 -2,4; ; ; part 5: multiple abort flags --------------------------------------- ; possible: anr + ale (non-resident + length) ; ard + ale (read-only + length) ; Summary: ; 5000$: tst (r2)+ ; r ; dst anr+ale 1 ; pdr= 0.,0,000 ; 5100$: clr (r3)+ ; w ; dst anr 1 ; pdr= 0.,0,000 ; 5200$: com (r4)+ ; m ; dst ale+ard 1 ; pdr= 0.,0,aro ; ; non-resident + length abort 5000$: clr kipdr6 ; plf= 0.;ed=0;acf=nres mov #5010$,vhvmmu mov #140102,r2 tst (r2)+ ; will fail halt 5010$: .word m0.anr!m0.ale!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000000010010 ; mmr1 +2,2 ; ; non-resident abort + write --> nres only 5100$: mov #5110$,vhvmmu mov #140002,r3 clr (r3)+ ; will fail halt 5110$: .word m0.anr!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000000010011 ; mmr1 +2,3 ; ; length + read-only abort 5200$: mov #<0.*md.plf>!md.aro,kipdr6 ; plf= 0.;ed=0;acf=r mov #5210$,vhvmmu mov #140102,r4 com (r4)+ ; will fail halt 5210$: .word m0.ale!m0.ard!<6*m0.pno>!m0.ena ; mmr0 ; dddddrrrdddddrrr .word ^b0000000000010100 ; mmr1 +2,4 ; 9000$: reset ; mmu off ;! MMU off clr cp.psw mov #<127.*md.plf>!md.arw,kipdr6 ; restore kernel mapping mov #v..mmu+2,v..mmu ; restore mmu catcher clr v..mmu+2 9999$: iot ; end of test C2.1 ; ; Section D: mmr2+mmr1+mmr0 register, abort recovery ========================= ; ; Test D1: ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; ; Test D1.1 -- code in user mode with D space, simulated SP extend +++ ; The code is started with short stack page, stack push fails, ; the kernel handler will to stack extension and register roll back. ; td0101: ; set up emt handler mov #vhuemt,v..emt clr v..emt+2 ; pr0 kernel ; set up mmu handler mov #2000$,v..mmu mov #cp.pr7,v..mmu+2 ; set up user mode pdr/par; short code/data page 0 ; short stack page 1, base 140000, length 1 click (plf=127.) --> 157700:157776 vc2sek = 157700 ; initial end of stack in kernel view vc2seu = 037700 ; initial end of stack in user view ; mov #<8.*md.plf>!md.arw,uipdr0 mov #,uipar0 mov #<8.*md.plf>!md.arw,udpdr0 mov #,udpar0 mov #<127.*md.plf>!md.arw!md.dwn,udpdr1 mov #<140000/100>,udpar1 ; enable mmu mov #m3.dum,mmr3 ; user d dspace, no 22bit mov #m0.ena,mmr0 ; enable mmu ;! MMU 18 ; ; run code vc2 in user mode -------------------------------- ; ; clear stack area seen by user code clr vc2sek-4 clr vc2sek-2 clr vc2sek clr vc2sek+2 ; clear mmr0:mmr2 save area clr 3000$ clr 3001$ clr 3002$ ; start code in user mode mov #1000$,vhustp ; set up continuation address mov #,-(sp) ; next psw: user mode clr -(sp) ; will start at 0 rti ; and launch it halt 1000$: ; continuation point hcmpeq r5,#0 ; ran sob to end ? mfpd sp ; get user SP hcmpeq (sp)+,#vc2seu-4 ; check expected value hcmpeq vc2sek+2,vc2dat ; 1st push hcmpeq vc2sek+0,vc2dat+2 ; 2nd push hcmpeq vc2sek-2,vc2dat+4 ; 3rd push hcmpeq vc2sek-4,vc2dat+6 ; 4th push ; ; SimH wrongly sets m0.ico, skip mmr0 check for SimH cmpb systyp,#sy.sih beq 1010$ hcmpeq 3000$,#!m0.ena> ; 1010$: hcmpeq 3001$,#^b1111011000010100 ; -2,sp;2,r4 hcmpeq 3002$,# ; ; reset user mode pdr/par clr uipdr0 clr uipar0 clr udpdr0 clr udpar0 clr udpdr1 clr udpar1 ; reset ; mmu off ;! MMU off mov #v..emt+2,v..emt ; restore emt catcher clr v..emt+2 mov #v..mmu+2,v..mmu ; restore mmu catcher clr v..mmu+2 jmp 9999$ ; ; the MMU trap handler ; - saves all registers (starting with PC dummp) ; - rolls back register changes seen in MMR1 ; - increases the stack by one click ; - restore all registers ; - restarts the aborted instruction by setting PC to MMR2 ; 2000$: mov mmr0,3000$ ; record mmr0:mmr2 mov mmr1,3001$ mov mmr2,3002$ ; save all registers clr -(sp) ; save dummy PC mfpd sp ; save pm SP push r5 ; and r5..r0 push r4 push r3 push r2 push r1 push r0 ; roll back register changes mov mmr1,r0 ; get mmr1 mov #2,r1 ; handle both halfes 2100$: mov r0,r2 bic #^c7,r2 ; mask out regnum field asl r2 ; word offset add sp,r2 ; address of register on stack movb r0,r3 asr r3 asr r3 asr r3 ; register correction sub r3,(r2) ; and correct register swab r0 ; go for 2nd half sob r1,2100$ ; and loop ; increase stack by one click --> decrease(!) plf mov #<126.*md.plf>!md.arw!md.dwn,udpdr1 ; restore all registers pop r0 ; restore r0..r5 pop r1 pop r2 pop r3 pop r4 pop r5 mtpd sp ; restore pm SP tst (sp)+ ; pop dummy PC ; roll back PC to re-run aborted instruction mov mmr2,(sp) ; roll back PC bic #,mmr0 ; clear abort bits rti ; return and restart instruction ; 3000$: .word 0 ; save mmr0 3001$: .word 0 ; save mmr1 3002$: .word 0 ; save mmr2 ; 9999$: iot ; end of test D1.1 ; END OF ALL TESTS - loop closure ============================================ ; mov tstno,r0 ; hack, for easy monitoring ... hcmpeq tstno,#11. ; all tests done ? ; jmp loop ; ; kernel handlers ============================================================ ; ; vhemmu - expected mmu abort/trap handler +++++++++++++++++++++++++++++++++++ ; used to catch expected MMU aborts or traps ; the pointer to expected mmr0/mmr1 values must be in vhvmmu ; code will continue after context ; execution will clear vhvmmu ; --> vhvmmu must be set for each execution ; the handler uses and modifies r0,r1 ; --> tests should only use r2,...,r5 ; vhemmu: mov vhvmmu,r1 ; get context beq 1000$ ; if 0 halt mov mmr0,r0 bic #m0.ico,r0 ; mask ico (for Simh compatibility) hcmpeq r0,(r1)+ ; check mmr0 hcmpeq mmr1,(r1)+ ; check mmr1 bic #m0.anr!m0.ale!m0.ard,mmr0 ; reset mmr0 abort flags mov r1,(sp) ; set up kernel return address clr vhvmmu ; reset context rti ; end return to continuation address 1000$: halt vhvmmu: .word 0 ; context pointer ; ; vhuemt - emt handler, drop frame, continue in kernel mode ++++++++++++++++++ ; use to end user/supervisor mode code with an emt xxx ; the kernel continution address must be written to vhustp ; execution will reset vhustp to a catcher value ; --> vhustp must be set for each execution ; vhuemt: tst (sp)+ ; discard one word of vector push mov vhustp,(sp) ; set up kernel return address mov #vhuhlt,vhustp ; reset stop address by catcher rts pc ; end return to continuation address vhustp: .word vhuhlt vhuhlt: halt ; ; Test codes that will be mapped in user or supervisor mode ================== ; They are located at 100000 and above and are position-independent code. ; That allows to assemble and load them together with the main code. ; ; vc0 - simple code ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; uses jsr, has stack below 1000 (no problem in user/supervisor mode) ; does operations with vc0v0, vc0v1, vc0v2 ; these location are usually set before and checked afterwards in kernel mode ; . = 100000 vc0: jmp 100$ .blkw 14. ; small stack space 100$: mov #40,sp ; initialize stack call 1000$ emt 100 ; will end user/supervisor code ; 1000$: com vc0v0 call 2000$ return 2000$: add vc0v1,vc0v2 return ; vc0v0: .word 0 vc0v1: .word 0 vc0v2: .word 0 ; ; vc1 - simple I/D code ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; uses jsr, has stack below 1000 (no problem in user/supervisor mode) ; does operations with vc1v0, vc1v1, vc1v2 ; these locations are usually set before and checked afterwards in kernel mode ; . = 101000 ; I space ------------------------------------ vc1: mov #,sp ; initialize stack mov #,r5 ; initialize data pointer call 1000$ emt 100 ; will end user/supervisor code ; 1000$: com (r5) ; will access vc1v0 call 2000$ return 2000$: add 2(r5),4(r5) ; will access vc1v1 and vc1v2 return ; vc1ida: .word 0 ; I space location, M*PI read/write target ; . = 102000 ; D space ------------------------------------ vc1dat: .blkw 16. ; small stack space vc1stk: vc1v0: .word 0 vc1v1: .word 0 vc1v2: .word 0 ; ; vc2 - stack push I/D code ++++++++++++++++++++++++++++++++++++++++++++++++++ ; set SP just above the stack page end; push data ; expect kernel handler to extend the stack and re-run failed push ; . = 103000 ; I space ------------------------------------ vc2: mov #vc2seu+4,sp ; 4 bytes above initial end mov #,r4 ; initialize data pointer to vc2dat mov #4.,r5 ; set loop count vc2l1: push (r4)+ ; push data sob r5,vc2l1 emt 100 ; will end code ; . = 104000 ; D space ------------------------------------ vc2dat: .word 010111 .word 010222 .word 010333 .word 010444 ; .end start