if1 [ ; Set flag if not being .insrt'd, i.e. mainline. %%main== %%incl==1 ; also assume need TITLE, ac defs, etc, ife %%main,ifdef p,ifdef a,%%incl==0 ; correct mistaken idea if don't need. ] ; Note can want "%%incl" type stuff even when not mainline, ; e.g. if program .insrt's TIMER and expects it to do all the ; random setup stuff while program merely specifies experiments. ifn %%incl,{ title timer irp ac,,[f,a,b,c,d,e,chk,xpr,cnt,r11,oc,u1,u2,u3,u4,p] ac=:.irpcnt termin P=:17 R1==:1 ? R2==:2 ? R3==:3 ; for JSYSes $$OUT==1 ; New output package! $$OUUO==0 ; Don't use output UUO's .insrt macros ; should be KSC; or whatever ifn os%tnx,.decsav tyoc==:1 tmpc==:16 loc 200 ; leave 100 wds room for hacks. $$OFLT==1 ; Floating-point output $$OTIM==1 ; Use time-output items $$OERR==1 ; Get sys err output stuff ERRCHN==17 .insrt out ;ifn os%its,.insrt timrts ; Needed for $$OTIM ifn os%its,.insrt syseng;datime ; Needed for $$OTIM pat: block 200 pdl: -100,,. block 100 } ;ifn %%incl ifndef junk, junk==:. ; for wrandom writes block 2 klsw: 0 ; -1 if running on a KL, meaning ITS can use KLPERF. kasw: 0 ; -1 if running on a KA hpsw: 0 ; -1 if running on TNX and want to use HPTIM. time1: 0 time2: 0 ; for holding 2nd word of KL perf counter klpfwd: 1502,,1400 ; Mode word to use for setting KLPERF klpfrl: 0 ; REAL mode word to use... klpfwd#74 ; Settable parameters loops: ifn os%tnx, 10000. ; TNX timing precision is terrible .else 500000. ncheks: 10. outmod: 0 ; 0 = output results at once, 1 = only at end. outful: 1 ; 1 = output full results comment \ XPER experiment definition macro Basic format: XPER , "Name of test", ||, ,[] - Given symbol will be set to the index of this experiment. This is useful for referencing a particular test, as wehn furnishing a in some other test. Can be left unspecified by just giving a comma. " ... " - Name of experiment. Will be printed out. || - Actual instruction to execute for testing. It is inserted at symbolic location INSTR, and falls through to symbolic location INSRET. It should not skip, and if it jumps should return to INSRET. It may use acs A-E,U1-U4 freely; others must be preserved intact if used. JUNK is available as a memory reference location for reads or writes. - Can be omitted for the usual case; it defaults to 0, which is the basic control loop. The time used by the specified control loop is subtracted from the total time used by this test in order to derive the actual time used only by some specific operation. - Can be omitted if the instruction requires no special setup. But if present, square brackets must enclose the code as it is taken as a literal. It is called by a PUSHJ and thus must return by a POPJ when done. Note that this is only invoked once before each check, not each time the instruction is about to be executed. \ define xper sym,&name&,*ins*,ctlidx,?setup ifle maxxpr-%%xpri, .err Too many xperiments. if1 ifsn [sym][] sym==%%xpri ; def a sym for this xper tmploc xprnam+%%xpri,{.length name,,[ascii name]} tmploc xprins+%%xpri,{ins} tmploc xprini+%%xpri,{setup} ifsn [ctlidx][] tmploc xprclp+%%xpri,{ctlidx} %%xpri==%%xpri+1 if1 %%nxpr==%%xpri termin %%xpri==0 ifndef maxxpr, maxxpr==20 xprnam: block maxxpr ; ascnt ptr to name xprini: block maxxpr ; setup stuff xprins: block maxxpr ; instruction to execute xpravg: block maxxpr ; avg time per check xprdev: block maxxpr ; deviation xprvar: block maxxpr ; variance xprclp: block maxxpr ; index of xperiment considered "control" for this. xprcor: block maxxpr ; avg time corrected for control loop xprtpi: block maxxpr ; resulting avg time per instruction xprdvi: block maxxpr ; resulting std dev per instr. ; Samples, to set up baseline params. xper ,|Basic control loop|,|sojle cnt,insret|,0,[setz a, ? setz b, ? popj p,] xper xmvi,|MOVEI 0|,|movei| xper xidx,"Indexing",|movei (a)|,xmvi xper xmve,"MOVE 70",|move 70| ifn os%tnx,[ define .value ?barf jsr tnxval termin tnxval: 0 out(tyoc,(".VALUE at "),rhv(tnxval),eol) haltf jrst @tnxval ; if continued, return ] ;ifn tnx syserr: out(tyoc,("Ugh, sys error return! - "),err,eol) .value ret .cpu==0 define acpu sym,&str& .cpu!sym==.cpu %cpu!sym==<1_.cpu> .cpu==.cpu+1 [asciz str] termin cputab: acpu ka,"KA-10" acpu ki,"KI-10" acpu kl,"KL-10" ; How to distinguish KL-10 and KL-20? Any diff? acpu 20,"KL-20" acpu ks,"KS-10" acpu f2,"F-2/3" ; How to distinguish? acpu 6,"PDP-6 (??!!)" .scalar cpu.x .scalar cpu.xf ; Flags cpufnd: jfcl 17,.+1 ; Clear flags jrst .+1 ; Change PC jfcl 1,[movei a,.cpu6 ; PDP-6 has PC change flag ret] movni a,1 ; Make AC all 1's aobjn a,.+1 ; Increment both halves jumpn a,[ ; KA if AC = 1,,0 move a,[010700,,-1] ; but perhaps also Foonly... ibp a tlne a,17 ; Ensure X not overflowed into jrst [ movei a,.cpuka ; Overflow = KA ret] movei a,.cpuf2 ret] ; KI, KL or KS if AC = 0 (no carry) blt a, jumpe a,[movei a,.cpuki ; KI if AC not = 1,,1 ret] ; Either a KL or KS. Perform IDIV test. push p,b movsi a,(setz) ; Set up -2^35 as dividend seto b, ; Set up -1 as divisor idivm a,b ; KL gets error, doesn't do it.... came b,[-1] ; So if B remains same, it's a KL. skipa a,[.cpuks] ; B got clobbered, it's a KS-10! movei a,.cpukl pop p,b ret .scalar aprid .scalar sysstr(20.) ; Allow big sysver string .os10x==1 ; Not really used yet .os20x==2 sysfnd: setzm aprid ifn os%its,[ syscal sstatu,[repeat 5,[ ? movem a] ? movem b] .lose %lssys out(tmpc,open(uc$bpt,[440700,,sysstr]),("MIT-"),6f(b),c(0)) ] ;ITS ifn os%tnx,[ IFN 0,[ ; One way of determining OS which doesn't work on some places. MOVE A,[112,,11] ; Magic word that will win on 10X,T20 (and maybe T10) GETTAB=<047000,,41> ; CALLI 41 GETTAB A, ; Returns 10000 T10, 20000 ITS, 30000 10X, 40000 T20 MOVEI A,30000 ; Shouldn't ever fail, but if it does, assume 10X. ]; IFN 0 MOVE A,[SIXBIT /SYSVER/] SYSGT MOVEI C,SYSSTR SYSFN2: MOVE A,C HRRI A,(B) GETAB SETZ A, MOVEM A,(C) JUMPN A,[AOBJP C,SYSFN2] SETZM APRID MOVE A,[SIXBIT /APRID/] ; See if system groks APRID SYSGT JUMPE B,[MOVEI A,.OS10X ; If not, definitely a 10X RET] MOVEM A,APRID MOVEI A,.OS20X RET IFN 0,[ ; Another way of determining if 10X or 20X SYSCAL SYSGT,[['LOADTB]][A ? D] SKIPN D ; If LOADTB is not defined TLO FF,FL20X ; it must be a Twenex ] ] ret ; Find machine type sysini: push p,a ? push p,b call cpufnd movem a,cpu.x movei b,1 lsh b,(a) ; Get flag bit for CPU movem b,cpu.xf ; Store too. setzm klsw cain a,.cpukl setom klsw ; yep, a KL. call sysfnd ; Find system type pop p,b ? pop p,a ret go: move p,pdl ; move a,[uuocal] ; set up (may have been clobbered if REL-file) ; movem a,41 call sysini ; Initialize for machine/system ifn os%its,[ .open tyoc,[.uao,,'tty] .lose 1000 out(tyoc,open(uc$iot)) ] ifn os%tnx, out(tyoc,open(uc$iot,[.priou])) out(,ch(tyoc)) ; Make TYOC the std output ifn os%tnx,[ movei a,.fhslf gtrpi ; get paging info out(,("Pgflts: "),d(a),(":"),d(b),(":"),d(c),eol) ] call setup ; Do basic setup for all experiments. skipn outmod call presho call doxprs ; Do all experiments skipe outmod call show ; Show results ifn os%its,[ .close tyoc, ; in case translating to dsk skipe klsw jrst [ syscal klperf,[cimm %jself ? cimm 0] .lose jrst .+1] .value [asciz /: Done î/] ] ;ITS ifn os%tnx,[ movei a,.fhslf gtrpi ; get paging info out(,("Pgflts: "),d(a),(":"),d(b),(":"),d(c),eol) out(,("Done!"),eol) haltf ] ;TNX jrst .-1 subttl Setup setup: pushae p,[a,b,c,d] move a,ncheks ; # checks of instr fsc a,233 movem a,fnchks move a,loops ; # loops per check fsc a,233 movem a,floops ; Drop thru ; Set up time-getting instructions as close as possible ; to instruction loop. move a,[nop] movsi b,lpbeg-lpbege ; AOBJN to prelim instrs movem a,lpbeg(b) ; Set all to nops aobjn b,.-1 movsi b,lpend-lpende ; AOBJN to postlim instrs movem a,lpend(b) ; also set all to nops. aobjn b,.-1 ifn os%its,[ move d,[trni,,trnf] skipe klsw ; If can use KLPERF stuff, jrst [ move a,klpfwd ; Record # ticks in user mode xor a,[74,,] ; hardware idiocy movem a,klpfrl ; Store REAL mode word for klperf call move d,[tklpi,,tklpf] jrst .+1] ] ifn os%tnx,[ move d,[trn10i,,trn10f] skipe hpsw move d,[thp10i,,thp10f] ] hlrz c,d ; Get addr of begin stuff movei b,lpbege sub b,(c) ; Find addr to start storing stuff at call set30 movei c,(d) ; Get addr of end stuff movei b,lpend ; Drop thru & return call set30 popae p,[d,c,b,a] ret set30: movn a,(c) hrl c,a addi c,1 ; Now have AOBJN to stuff. set40: move a,(c) ; Get word of stuff movem a,(b) ; Store addi b,1 aobjn c,set40 ret ifn os%tnx,[ trn10i: 0 call [ pushae p,[r1,r2,r3] ; Stupid call clobbers 3 ACs movei r1,.fhslf runtm movem r1,time1 popae p,[r3,r2,r1] ret] %l==. ? tmploc trn10i,<%l-1-trn10i> trn10f: 0 movei r1,.fhslf runtm ifn a-r1,move a,r1 sub a,time1 imuli a,1000. ; Get usec jrst havtim %l==. ? tmploc trn10f,<%l-1-trn10f> thp10i: 0 call [ push p,r1 movei r1,1 ; 1 = .hprnt hptim ; JSYS 501 ercal syserr exch r1,(p) pop p,time1 ret] %l==. ? tmploc thp10i,<%l-1-thp10i> thp10f: 0 movei r1,1 hptim ercal syserr ifn r1-a,move a,r1 sub a,time1 imuli a,10. jrst havtim %l==. ? tmploc thp10f,<%l-1-thp10f> ] ;TNX ifn os%its,[ trni: 0 .suset [.rrunt,,time1] %l==. ? tmploc trni,<%l-1-trni> trnf: 0 .suset [.rrunt,,a] sub a,time1 lsh a,2 jrst havtim %l==. ? tmploc trnf,<%l-1-trnf> tklpi: 0 syscal klperf,[ cimm %jself ? klpfrl repeat 4,[cret junk ? ] cret time1 ? cret time2] .value [asciz /: Can't grab KL perf counter! î/] %l==. ? tmploc tklpi,<%l-1-tklpi> tklpf: 0 syscal klperf,[ ; No input args. repeat 4,[cret junk ? ] cret a ? cret b] .value [asciz /: Argh! Lossage with perf counter! î/] jrst kltim %l==. ? tmploc tklpf,<%l-1-tklpf> ; Jump here after finding time at end of loop. kltim: ashc a,-12. ; Shift over dmove c,time1 ; get original counter ashc c,-12. ; shift that over too. dsub a,c ; OK, have at it. move a,b ; leave result in A addi a,<<1000./80.>/2> ; basic tick time is 80 nsec idivi a,<1000./80.> ; Find # of usec (round off) jrst havtim ; Have loop runtime, go process it. ] ;ifn its ; Do all experiments doxprs: movsi xpr,-maxxpr call doxper ; Do experiment aobjn xpr,.-1 ; do them all. ret ; Do an experiment... ncheks times, at nloops xct's per check. doxper: skipn xprnam(xpr) ; See if something there. ret ; if none, skip it. move a,xprins(xpr) ; Get instr movem a,instr ; store into loop setzm tottim ; Clear total time for experiment. setzm ftot2 ; also stuff for variance. movn chk,ncheks ; Set up for check loop hrlzs chk cheklp: move cnt,loops skipe a,xprini(xpr) pushj p,(a) ; do idiosyncratic setup if one. jrst lpbeg skpins: skptst: setzm tottim ; Jump here to skip whole test setzm ftot2 jrst tstend ; Here's the timing loop.... lpbeg: nop ; For setting up shutter-click instructions. nop lpbege: nop INSTR: jfcl ; LOOP: Instruction is inserted here INSRET: sojg cnt,instr ; LOOP: Loop fast, fall thru when done. lpend: nop ; For setting up shutter-done instructions. nop nop nop lpende: nop sub a,time1 ; get time eaten in loop. ifn os%its, lsh a,2 ; ITS: multiply by 4 to get # usec ifn os%tnx, imuli a,10. ; TNX: multiply by 10 havtim: addm a,tottim ; and add to total. fsc a,233 fmpr a,a fadrm a,ftot2 ; add to total**2 aobjn chk,cheklp tstend: pushj p,scntms ; ok, figure stats from time table. skipn outmod calret scnsho ; Show stats for this experiment. ret ; SCNTMS - Compute statistics for experiment. scntms: skipge a,tottim .value ; Negative time used?!?! fsc a,233 movem a,ftotim fdvr a,fnchks movem a,xpravg(xpr) ; average # usecs ; Calculate variance skipge a,ftot2 .value ; Negative time?!?!?! fdvr a,fnchks ; total(X^2) / n move b,xpravg(xpr) fmpr b,b ; - avg^2 camge a,b ; something funny has been happening. setzb a,b ;.value ; Precision (or lack thereof) sometimes loses. fsbr a,b ; now have variance movem a,xprvar(xpr) ; store variance pushj p,sqrt ; get std dev movem a,xprdev(xpr) ; store that. fdvr a,floops ; find std dev per single pass movem a,xprdvi(xpr) ; store as dev per instr skipn a,xpravg(xpr) jrst scntm6 ; If avg time per chk was 0, test was skipped. move b,xprclp(xpr) ; get idx for control loop trne xpr,-1 ; Skip if this is Master control loop fsbr a,xpravg(b) ; get corrected time (minus selected ctl loop) scntm6: movem a,xprcor(xpr) fdvr a,floops ; find time per instruction movem a,xprtpi(xpr) ; time per ins execution (we hope) popj p, ; all for now floops: 0 ; Floating # of executes per check fnchks: 0 ; Floating # of checks tottim: 0 ; Total time used in expt ftotim: 0 ; Floating total time ftot2: 0 ; for variance ; Done with timings, now can output cruft. show: ; move a,[uuocal] ; movem a,41 ; set up properly. call presho ; Do prelim stuff movsi xpr,-maxxpr showlp: skipe xprnam(xpr) call scnsho aobjn xpr,showlp ret syssho: move a,cpu.x out(,("CPU: "),tz(@cputab(a))) skipe a,aprid outcal(,(" "),LPAR,("ID: "),d(a),RPAR) out(,(" SYS: "),tz(sysstr)) ret presho: out(,("TIMER test of "),tim(mdyt),eol,call(syssho),eol) out(,("Each test: "),d(ncheks),(" samples, "),d(loops),(" executes per sample. +-- Test number | +-- Correction test no. (default 0) | | | | Average Std Average uncorrected #:(#) Name-of-test usec/xct Dev (usec/sample std-dev %-sd) ")) ret scnsho: call scnsh3 ; out(,fmt(call(scnsh3),-25.)) skipn xpravg(xpr) ; Ensure this test not skipped jrst [ out(,(" --- Skipped ---"),eol) ret] out(,(" "),f(xprtpi(xpr),7,3),(" "),f(xprdvi(xpr),7,4)) trne xpr,-1 ; If control test skipe outful ; or asking for everything, then add stuff. jrst [out(,(" "),LPAR,f(xpravg(xpr)),(" "),f(xprdev(xpr))) move a,xprdev(xpr) fmpr a,[100.0] fdvr a,xpravg(xpr) ; Find pct that std dev amounts to out(,(" "),f(a,4,1),("%"),RPAR) jrst .+1] out(,eol) ret scnsh3: movei a,(xpr) out(,d(a,2),(":")) skipe b,xprclp(xpr) jrst [ out(,LPAR,d(b),RPAR) caige b,10. ; Compute extra cols used skipa b,[3] movei b,4 jrst .+1] out(,(" ")) skipn a,xprnam(xpr) move a,[1,,ascii /?/] out(,fmt(tc(a),-20.(b))) ret ; Famous AGB sqrt routine. clobbers B, C. Ignores neg #'s. SQRT: SKIPG B,A POPJ P, ASH A,-1 ADD A,[262370613] ; 0.292893/0.840186 B8. ; or 0.414214/0.594101 B9. TLON A,400 JRST SQRT2 FMPRI A,301460 ; 0.594101^101 JRST SQRT3 SQRT2: FMPRI A,300656 ; 0.840186^100 SQRT3: MOVE C,B FDV B,A FAD A,B ? FSC A,-1 FDVR C,A FAD A,C ? FSC A,-1 ; MORE EXACT THAN FADR POPJ P, ife %%main,.ineof end go