; $Id: cpu_details.mac 1305 2022-10-23 07:44:21Z mueller $ ; SPDX-License-Identifier: GPL-3.0-or-later ; Copyright 2022- by Walter F.J. Mueller ; ; Revision History: ; Date Rev Version Comment ; 2022-07-28 1264 1.0 Initial version ; 2022-07-18 1259 0.1 First draft ; ; Test CPU details ; Section A: CPU registers ; Section B: stress tests ; Section C: 11/70 specifics ; .include |lib/tcode_std_base.mac| .include |lib/defs_mmu.mac| ; ; Preface: set up MMU for kernel mode (for some tests) ======================= ; 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) ; ; some useful definitions kipdr5 = kipdr+12 kipdr6 = kipdr+14 kipar6 = kipar+14 p6base = <6*20000> ; page 6 ; ; Section A: CPU registers =================================================== ; ; Test A1: PIRQ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; This sub-section verifies operation of PIRQ register ; ; Test A1.1 -- PIRQ + spl ++++++++++++++++++++++++++++++++++++++++++++ ; This test will exercise all 7 pirq interrupt levels: ; set 1+3 -> handle 3, set 7 ; -> handle 7, set 6+4 ; -> handle 6 ; -> handle 4, set 5+2 ; -> handle 5 ; -> handle 2 ; -> handle 1 ; ; some useful definitions pi.r00=bit08 ; pir 0 bit pi.r01=bit09 ; pir 1 bit pi.r02=bit10 ; pir 2 bit pi.r03=bit11 ; pir 3 bit pi.r04=bit12 ; pir 4 bit pi.r05=bit13 ; pir 5 bit pi.r06=bit14 ; pir 6 bit pi.r07=bit15 ; pir 7 bit pi.n00=0. ; lsb for no pir pending pi.n01=1*042 ; lsb for pir 1 next pi.n02=2*042 ; lsb for pir 2 next pi.n03=3*042 ; lsb for pir 3 next pi.n04=4*042 ; lsb for pir 4 next pi.n05=5*042 ; lsb for pir 5 next pi.n06=6*042 ; lsb for pir 6 next pi.n07=7*042 ; lsb for pir 7 next ; ta0101: mov #1000$,v..pir ; setup handler mov #cp.pr7,v..pir+2 ; which runs at pr7 mov #cp.pir,r3 ; ptr to PIRQ mov #cp.psw,r4 ; ptr to PSW mov #1200$,r5 ; ptr to check data clr 1300$ ; clear nesting counter ; spl 7 ; lockout interrupts bisb #bit01,1(r3) ; set PIRQ 1 hcmpeq (r3),# ; check set 1 bisb #bit03,1(r3) ; set PIRQ 3 hcmpeq (r3),# ; check set 1+3 spl 2 nop ; allow interrupts to happen spl 0 nop ; allow interrupts to happen htsteq (r3) ; PIRQ should clear now mov #v..pir+2,v..pir; restore pirq vector catcher clr v..pir+2 jmp 9999$ ; ; PIRQ interrupt handler ; - it starts at pr7 from the vector ; - quickly lowers the priority to what is currently processed ; - new pirq bits are set at lowered priority ; - that leads to nested interrupts (tracked by a level counter) ; 1000$: inc 1300$ ; up level counter mov (r3),r0 ; get PIRQ value hcmpeq 1300$,(r5)+ ; check nesting level hcmpeq r0,(r5)+ ; check pirq value movb r0,(r4) ; PSW=PIRQ (sets priority) bic #177761,r0 ; mask out index bits mov r0,r1 ; r0 is word index (pri*2) asr r1 ; r1 is byte index (pri*1) mov #pi.r00,r2 ash r1,r2 ; r2 = pi.r00 <<(pri) bic r2,(r3) ; clear current level in pirq bis 1100$(r0),(r3) ; trigger new pirqs nop ; allow nested interrupts to happen nop ; dec 1300$ ; down level counter rti ; ; table with new pirqs triggered at a level 1100$: .word 0 ; new pirq @ level 0 .word 0 ; new pirq @ level 1 .word 0 ; new pirq @ level 2 .word pi.r07 ; new pirq @ level 3 -> 7 .word pi.r05!pi.r02 ; new pirq @ level 4 -> 5+2 .word 0 ; new pirq @ level 5 .word 0 ; new pirq @ level 6 .word pi.r06!pi.r04 ; new pirq @ level 7 -> 6+4 ; ; table with expected values of pirq register in interrupt sequence 1200$: .word 1,pi.r01!pi.r03!pi.n03 ; set 1+3 -> handle 3, set 7 .word 2,pi.r01!pi.r07!pi.n07 ; set 1+7 -> handle 7, set 6+4 .word 2,pi.r01!pi.r04!pi.r06!pi.n06 ; set 1+4+6 -> handle 6 .word 2,pi.r01!pi.r04!pi.n04 ; set 1+4 -> handle 4, set 5+2 .word 3,pi.r01!pi.r02!pi.r05!pi.n05 ; set 1+2+5 -> handle 5 .word 1,pi.r01!pi.r02!pi.n02 ; set 1+2 -> handle 2 .word 1,pi.r01!pi.n01 ; set 1 -> handle 1 ; nesting level counter 1300$: .word 0 ; 9999$: iot ; end of test A1.1 ; ; Test A2: CPUERR +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; This sub-section verifies operation of CPUERR register ; ; setup for all A2.* tests mov #cp.err,r0 ; ptr tp CPUERR mov #vhugen,v..iit ; set iit handler clr v..iit+2 ; pr0 kernel ; ; Test A2.1 -- CPUERR cp.hlt +++++++++++++++++++++++++++++++++++++++++ ; Test cp.hlt: halt in non-kernel mode ; ta0201: mov #177777,(r0) ; clear CPUERR (any write should) hcmpeq (r0),#0 ; ensure that CPUERR is zero ; mov #4000$,r2 ; mode list (user,supervisor) mov #2,r3 ; number of modes ; 1000$: clr r1 ; clear tracer mov #3000$,vhustp ; continuation address push (r2)+ ; frame: psw push #2000$ ; frame: address rti ; start user mode code halt ; 2000$: inc r1 ; proof of execution halt ; that will abort inc r1 tst @#001 ; that must abort ; 3000$: hcmpeq r1,#1 ; check tracer hcmpeq (r0),#cp.hlt ; check CPUERR mov #cp.rsv,(r0) ; clear CPUERR (any write should) hcmpeq (r0),#0 ; check CPUERR ; sob r3,1000$ ; go for next mode ; br 9999$ ; 4000$: .word ; user mode .word ; supervisor mode ; 9999$: iot ; end of test A2.1 ; ; Test A2.2 -- CPUERR cp.aer +++++++++++++++++++++++++++++++++++++++++ ; Test cp.aer: address error abort ; ta0202: mov #1000$,vhustp ; continuation address tst @#001 ; odd address access halt 1000$: hcmpeq (r0),#cp.aer ; check CPUERR clr (r0) ; clear CPUERR (any write should) hcmpeq (r0),#0 ; check CPUERR ; 9999$: iot ; end of test A2.2 ; ; Test A2.3 -- CPUERR cp.nxm +++++++++++++++++++++++++++++++++++++++++ ; Test cp.nxm: non-existent memory abort ; Use unibus map address space (248kB) below the I/O page, the w11 ; will return a non-existent memory abort even in a maximum memory ; configuration. ; ta0203: cmpb systyp,#sy.e11 ; e11 V7.3 return wrong CPUERR value beq 9999$ push kipar6 ; save kipar6 mov #177400,kipar6 mov #m3.e22,mmr3 ; 22-bit mode mov #m0.ena,mmr0 ; enable mmu ;! MMU 22 ; mov #1000$,vhustp ; continuation address tst p6base ; access non-existing memory halt 1000$: hcmpeq (r0),#cp.nxm ; check CPUERR com (r0) ; clear CPUERR (any write should) hcmpeq (r0),#0 ; check CPUERR ; reset ; disable mmu ;! MMU off pop kipar6 ; restore kipar6 9999$: iot ; end of test A2.3 ; ; Test A2.4 -- CPUERR cp.ito +++++++++++++++++++++++++++++++++++++++++ ; Test cp.ito: unibus timeout abort ; Use first address in I/O page (160000), always unused in w11 ; ta0204: mov #1000$,vhustp ; continuation address tst @#160000 ; access non-existing unibus device halt 1000$: hcmpeq (r0),#cp.ito ; check CPUERR clr (r0) ; clear CPUERR (any write should) hcmpeq (r0),#0 ; check CPUERR ; 9999$: iot ; end of test A2.4 ; ; Test A2.5 -- CPUERR cp.ysv +++++++++++++++++++++++++++++++++++++++++ ; Test cp.ysv: yellow stack trap ; Since stack is still usable after the trap, the vhugen handler can be used. ; ta0205: cmpb systyp,#sy.e11 ; e11 V7.3 doesnt trap, runs on hold beq 9999$ mov #1000$,vhustp ; continuation address mov #400,sp clr -(sp) ; should trap (not abort) halt ; not executed, handler continues at 1000$ 1000$: hcmpeq (r0),#cp.ysv ; check CPUERR clr (r0) ; clear CPUERR (any write should) hcmpeq (r0),#0 ; check CPUERR ; mov #stack,sp 9999$: iot ; end of test A2.5 ; ; Test A2.6 -- CPUERR cp.rsv +++++++++++++++++++++++++++++++++++++++++ ; Test cp.rsv: red stack trap - simple low stack case ; ta0206: cmpb systyp,#sy.e11 ; e11 V7.3 doesnt abort, runs on hold beq 9999$ mov #1000$,v..iit ; setup direct iit handler mov #340,sp clr -(sp) ; should abort (not trap) halt 1000$: mov #stack,sp ; direct iit handler hcmpeq (r0),#cp.rsv ; check CPUERR clr (r0) ; clear CPUERR (any write should) hcmpeq (r0),#0 ; check CPUERR ; 9999$: iot ; end of test A2.6 ; ; Test A2.7 -- CPUERR cp.rsv+cp.aer (odd address) ++++++++++++++++++++ ; Test cp.aer: fatal stack error after odd stack ; ta0207: cmpb systyp,#sy.e11 ; e11 V7.3 pushes to odd stack, abort after halt beq 9999$ mov #1000$,v..iit ; setup direct iit handler mov #stack-1,sp clr -(sp) ; odd-address abort, fatal stack error halt 1000$: mov #stack,sp ; direct iit handler hcmpeq (r0),# ; check CPUERR clr (r0) ; clear CPUERR (any write should) hcmpeq (r0),#0 ; check CPUERR ; 9999$: iot ; end of test A2.7 ; ; Test A2.8 -- CPUERR cp.rsv+cp.nxm ++++++++++++++++++++++++++++++++++ ; Test cp.nxm: fatal stack error after non-existent memory abort ; Setup like in A2.3, put stack at p6base+4 ; ta0208: cmpb systyp,#sy.e11 ; e11 V7.3 pushes to bad stack, abort after halt beq 9999$ mov #1000$,v..iit ; setup direct iit handler mov #177400,kipar6 mov #m3.e22,mmr3 ; 22-bit mode mov #m0.ena,mmr0 ; enable mmu ;! MMU 22 ; mov #p6base+4,sp ; stack in non-existing memory clr -(sp) ; non-existing memory, fatal stack error halt 1000$: mov #stack,sp ; direct iit handler hcmpeq (r0),# ; check CPUERR clr (r0) ; clear CPUERR (any write should) hcmpeq (r0),#0 ; check CPUERR ; reset ;! MMU off mov #001400,kipar6 ; restore kipar6 ; 9999$: iot ; end of test A2.8 ; ; Test A2.9 -- CPUERR cp.rsv+cp.ito ++++++++++++++++++++++++++++++++++ ; Test cp.ito: fatal stack error after unibus timeout ; Setup like in A2.4, put stack at 160004 ; ta0209: cmpb systyp,#sy.e11 ; e11 V7.3 pushes to bad stack, abort after halt beq 9999$ mov #1000$,v..iit ; setup direct iit handler mov #160004,sp ; stack at non-existing unibus device clr -(sp) ; non-existing memory, fatal stack error halt 1000$: mov #stack,sp ; direct iit handler hcmpeq (r0),# ; check CPUERR clr (r0) ; clear CPUERR (any write should) hcmpeq (r0),#0 ; check CPUERR ; 9999$: iot ; end of test A2.9 ; ; Test A2.10 -- CPUERR cp.rsv+cp.aer (mmu abort) +++++++++++++++++++++ ; Test cp.rsv: fatal stack error after mmu abort ; Set kernel I page 6 to non-resident ; ta0210: cmpb systyp,#sy.sih ; this fatal stack error fails in SimH beq 9999$ cmpb systyp,#sy.e11 ; e11 V7.3 pushes to bad stack, does MMU 250 beq 9999$ mov #1000$,v..iit ; setup direct iit handler clr kipdr6 ; set non-resident mov #m3.e22,mmr3 ; 22-bit mode mov #m0.ena,mmr0 ; enable mmu ;! MMU 22 ; mov #p6base+4,sp ; stack in non-resident memory clr -(sp) ; MMU abort, fatal stack error halt 1000$: mov #stack,sp ; direct iit handler hcmpeq (r0),# ; check CPUERR clr (r0) ; clear CPUERR (any write should) hcmpeq (r0),#0 ; check CPUERR ; reset ;! MMU off mov kipdr5,kipdr6 ; restore kipdr6 (default kipdr are identical) ; 9999$: iot ; end of test A2.10 ; ; ----------------------------------------------- ; end of A2.* tests, restore iit handler mov v..iit+2,v..iit ; restore iit handler ; ; Section B: Stress tests ==================================================== ; ; Test B1: address mode torture tests +++++++++++++++++++++++++++++++++++++++ ; This sub-section tests peculiar address node usage ; ; Test B1.1 -- src-dst update hazards with (r0)+,(r0) ++++++++++++++++ ; tb0101: mov #2,r5 100$: mov #1000$,r0 mov #1110$,r1 push 1000$+2 ; save data that will change push 1000$+6 push 1100$+0 push 1100$+4 ; mov (r0)+,(r0)+ ; mov 111 over 222 add (r0)+,(r0)+ ; add 333 to 444 mov -(r1),-(r1) ; mov 444 over 333 add -(r1),-(r1) ; add 222 to 111 ; hcmpeq 1000$+2,#000111 hcmpeq 1000$+6,#000777 hcmpeq 1100$+4,#000444 hcmpeq 1100$+0,#000333 ; pop 1100$+4 ; restore data pop 1100$+0 pop 1000$+6 pop 1000$+2 sob r5,100$ jmp 9999$ ; 1000$: .word 000111 ; data for (r0)+ part .word 000222 .word 000333 .word 000444 ; 1100$: .word 000111 ; data for -(r0) part .word 000222 .word 000333 .word 000444 1110$: ; 9999$: iot ; end of test B1.1 ; ; Test B1.2 -- (pc)+ as destination ++++++++++++++++++++++++++++++++++ ; tb0102: mov #2,r5 100$: push 1000$+4 ; save data that will change push 1100$+4 push 1200$+2 ; clr r0 1000$: mov #1,#0 ; (pc)+,(pc)+: write #1 over #0 1100$: add #1,#2 ; (pc)+,(pc)+: add #1 to #2 mov 1000$+4,1200$+2 ; -14(pc),2(pc): dst of mov -> src of add 1200$: add #0,r0 ; add #1(!) to r0 ; hcmpeq 1000$+4,#1 hcmpeq 1100$+4,#3 hcmpeq r0,#1 ; pop 1200$+2 ; restore data pop 1100$+4 pop 1000$+4 sob r5,100$ ; 9999$: iot ; end of test B1.2 ; ; Test B1.3 -- pc as destination in clr, mov, and add ++++++++++++++++ ; tb0103: mov #000137,@#0 ; setup jmp 1000$ at mem(0) mov #1000$,@#2 clr pc halt halt halt 1000$: mov #1100$,pc halt halt halt 1100$: clr r0 add #4,pc ; skip two instructions inc r0 inc r0 inc r0 ; lands here inc r0 hcmpeq r0,#2 ; clr @#0 ; remove jmp 1000$ at mem(0) clr @#2 ; 9999$: iot ; end of test B1.3 ; ; Test B2: pipeline torture tests +++++++++++++++++++++++++++++++++++++++++++ ; This sub-section tests self-modifying code ; ; Test B2.1 -- self-modifying code, use (pc), -(pc) ++++++++++++++++++ ; tb0201: mov #2,r5 100$: mov 1000$,-(sp) mov 1100$,-(sp) ; clr r0 clr r1 clr r2 mov #005201,r3 ; r3= inc r1 mov #005202,r4 ; r4= inc r2 ; inc r0 mov r3,(pc) ; will overwrite next instruction 1000$: halt ; will be overwritten with 'inc r1' inc r0 1100$: mov r4,-(pc) ; will overwrite itself and re-execute(!) inc r0 ; hcmpeq r0,#3 ; 3 inc r0 in code hcmpeq r1,#1 ; check that 'inc r1' was executed hcmpeq r2,#1 ; check that 'inc r2' was executed ; mov (sp)+,1100$ mov (sp)+,1000$ sob r5,100$ ; 9999$: iot ; end of test B2.1 ; ; Test B2.2 -- self-modifying code, use (pc) case 2 ++++++++++++++++++ ; Was insprired by KDJ11A.MAC (J11 is indeed pipelined) ; tb0202: mov #2,r5 100$: mov 1000$,-(sp) mov 1100$,-(sp) mov 1200$,-(sp) ; mov #1999$,r1 clr r2 ; mov #005202,(pc) ; will replace jmp (r1) with 'inc r2' 1000$: jmp (r1) mov #005202,(pc) ; will replace jmp (r1) with 'inc r2' 1100$: jmp (r1) mov #005202,(pc) ; will replace jmp (r1) with 'inc r2' 1200$: jmp (r1) ; hcmpeq r2,#3 ; check that 'inc r2' was executed ; mov (sp)+,1200$ mov (sp)+,1100$ mov (sp)+,1000$ sob r5,100$ jmp 9999$ ; 1999$: halt ; halt as target for 'jmp (r1)' ; 9999$: iot ; end of test B2.2 ; ; Section C: 11/70 specifics ================================================= ; ; Test C1: Implementation differences +++++++++++++++++++++++++++++++++++++++ ; This sub-section verifies that w11 shows 11/70 behavior in cases ; were J11 and other CPU models show different behavior ; ; Test C1.1 -- Register used as source and changed in dst flow +++++++ ; OPR R,(R)+ : incremented before {J11} or after {70} use as source ; OPR R,-(R) : decremented before {J11} or after {70} use as source ; tc0101: mov #1000$,r4 mov r4,(r4)+ hcmpeq 1000$,#1000$ ; check r4 prior inc stored mov r4,-(r4) hcmpeq 1000$,#1000$+2 ; check r4 prior dec stored br 9999$ ; 1000$: .word 0 ; 9999$: iot ; end of test C1.1 ; ; Test C1.2 -- PC used as source +++++++++++++++++++++++++++++++++++++ ; OPR PC,A(R) : store PC+2 {70} or PC+4 {J11} ; tc0102: mov #1000$,r4 100$: mov pc,0(r4) hcmpeq 1000$,#100$+2 ; check pc after fetch stored br 9999$ ; 1000$: .word 0 ; 9999$: iot ; end of test C1.2 ; ; Test C1.3 -- Registers accessible via 177700-1777717 +++++++++++++++ ; CPU access to 177700-177717 (regs) timesout {70,J11} or not {05,10} ; tc0103: mov #vhugen,v..iit ; set iit handler clr v..iit+2 ; pr0 kernel ; mov #1000$,vhustp ; continuation address tst @#177700 ; should fail halt ; 1000$: mov #1100$,vhustp ; continuation address tst @#177716 ; should fail halt ; 1100$: mov v..iit+2,v..iit ; restore iit handler ; 9999$: iot ; end of test C1.3 ; ; END OF ALL TESTS - loop closure ============================================ ; mov tstno,r0 ; hack, for easy monitoring ... hcmpeq tstno,#19. ; all tests done ? ; jmp loop ; ; kernel handlers ============================================================ ; ; vhugen - generic handler for expected traps/abort ++++++++++++++++++++++++++ ; the kernel continution address must be written to vhustp ; execution will reset vhustp to a catcher value ; --> vhustp must be set for each execution ; vhugen: 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 ; .end start