// Package for analyzing micro-PC histograms // last edited December 19, 1980 2:21 PM external // defined here [ ReadLispPCHist // (instrm, hist) -> 0/string ScanMBForPCHist // (mbstrm[, zone]) -> 0/string PrintPCHist // (outstrm, hist) ] external [ // OS Allocate DoubleAdd; Dvec Endofs GetFixed; Gets MoveBlock Noop oneBits Puts ReadBlock SetBlock Usc WriteBlock; Ws; Wss Zero // ReadMB ReadMB // Template PutTemplate // DPDivide DPDIVIDE ] manifest [ lvMultiply = 343b // Bcpl Multiply subroutine lHist = 10000b // length of histogram lHE = 2 // length of histogram entry lLine = 96 // line width (assumes Gacha8 on display, // Gacha6 on printer) lLabel0 = 32 // width of label for Dolphin lLabel1 = 27 // width of label for Dorado starFactor = 2000 // proportion of total for one * upcEventType = 214b // "event type" in Lisp-format log file upcBlockSize = 64 // entries per block ] structure BS: [ length byte; char↑1,255 byte ] structure Event: [ type byte; arg byte ] structure MI0: // Dolphin microinstruction -- as output by MicroD [ w0 word = [ meminst bit 1 rmod bit 1 = df2 bit 1 rselhi4 bit 4 aluf bit 4 = type bit 4 [ bsel bit 2 f1 bit 4 ] = srcdesthi6 bit 6 ] w1 word = [ [ lr bit 1 lt bit 1 ] = srcdestlo2 bit 2 f2 bit 4 jc bit 3 jalo6 bit 6 parity bit 1 ] w2 word = [ rsello2 bit 2 jahi2 bit 2 blank bit 12 ] ] manifest [ f1LoadPage0 = 5 jcCall0 = 5 jcReturn0 = 6 ] structure MI1: // Dorado microinstruction -- as output by MicroD [ w0 word = [ rstk bit 4 aluf bit 4 bsel bit 3 lc bit 3 aselx2 bit 2 ] w1 word = [ asello1 bit 1 block bit 1 ff bit 8 jcn bit 6 = [ jclocal bit 2; jnlocalx4 bit 4 ] = [ jcglobal bit 2; jnglobalx4 bit 4 ] = [ jclong bit 4; jnlongx4 bit 2 ] = [ jcfast bit 1; jnfastx8 bit 2; jnfastx2 bit 2; jcondx4 bit 1 ] ] w2 word = [ jnlo bit 2 = jcondlo bit 2 brkp bit 2 blank bit 12 ] ] manifest [ jcLocal1 = 2 jcGlobal1 = 3 jcLong1 = 0 jcFast1 = 0 ] manifest [ lMI = 3 ] // width of microinstruction structure IFUM: // Dorado IFU memory -- as output by MicroD [ iw0 word = [ PA bit 6 // only uses low bit notIFADr2 bit 10 // not (address rshift 2) ] iw1 word = [ Sign bit 1 IPar bit 3 notLength bit 2 notRBaseB bit 1 MemB bit 3 notTPause bit 1 notTJump bit 1 N bit 4 ] ] manifest [ IFADr2mask = 1777b ] // complement notIFADr2 wrt this static [ imx = -1 // memory index of IM rimap // real to imaginary address map irmap // imaginary to real address map isymap // imaginary address to symbol map idata // contents of IM ifue // flags for IFU entries machine = -1 // 0 for Dolphin, 1 for Dorado mbzone // zone for allocating symbols special // procedure for machine-dependent output lLabel // machine-dependent label width nCalls // count # of call instructions ] let ReadLispPCHist(in, hist) = valof [ Zero(hist, lHist*lHE) until Endofs(in) do [ let event = Gets(in) let type = event<<Event.type test type eq upcEventType ifso [ let block = Gets(in) if (event<<Event.arg ne upcBlockSize*lHE) % (Usc(block, lHist/upcBlockSize) ge 0) then resultis "Illegal data file" ReadBlock(in, hist+block*upcBlockSize*lHE, upcBlockSize*lHE) ] ifnot switchon type rshift 6 into [ case 2: for i = 0 to event<<Event.arg do Gets(in); endcase case 3: Gets(in); Gets(in) case 1: Gets(in) case 0: ] ] resultis 0 ] and ScanMBForPCHist(mbin, zone; numargs na) = valof [ let allocfixed(zone, n) = GetFixed(n) if na ls 2 then zone = lv allocfixed mbzone = zone imx, machine = -1, -1 rimap = Allocate(zone, lHist) // Real to imaginary address map SetBlock(rimap, -1, lHist) irmap = Allocate(zone, lHist) // Imaginary to real address map SetBlock(irmap, -1, lHist) isymap = Allocate(zone, lHist) // Imaginary to symbolic address map Zero(isymap, lHist) isymap!0 = "" idata = Allocate(zone, lHist*lMI) // contents of IM ifue = Allocate(zone, lHist/64) // IFU entry flags, only every 4th instruction Zero(ifue, lHist/64) resultis ReadMB(mbin, 20, pmemproc, psymproc) ] and PrintPCHist(out, hist) be [ switchon machine into [ case 0: special, lLabel = special0, lLabel0; endcase case 1: special, lLabel = special1, lLabel1; endcase ] mainprint(out, hist) if machine eq 1 then callprint(out, hist) ] and mainprint(out, hist) be [ let total = vec 2 total!0, total!1 = 0, 0 let nonzero = 0 let entry = vec 2 for i = 0 to lHist-1 do [ let hp = hist+i*lHE entry!0, entry!1 = hp!1, hp!0 if (entry!0 ne 0) % (entry!1 ne 0) then [ nonzero = nonzero+1 DoubleAdd(total, entry) ] ] let q, r = vec 2, vec 2 let perStar = vec 2 DPDIVIDE(total, table[ 0; starFactor], perStar, r) if (perStar!0 eq 0) & (perStar!1 eq 0) then perStar!1 = 1 let maxStars = lLine-10-lLabel PutTemplate(out, "Total of $ED counts, $D nonzero entries; each ** = $ED counts*N*N", total, nonzero, perStar) let lastSym = 0 let cum = vec 2 cum!0, cum!1 = 0, 0 let count = vec 2 nCalls = 0 for imag = 0 to lHist-1 do [ let i = irmap!imag if i eq -1 loop let hp = hist+i*lHE if (hp!0 eq 0) & (hp!1 eq 0) loop let imsym = findsym(imag) let sym = isymap!imsym test sym eq lastSym ifso [ Wss(out, " ") printstars(out, count, perStar, maxStars) ] ifnot // print cumulative percent for old symbol [ if lastSym ne 0 then [ printpercent(out, cum, total) printstars(out, count, perStar, maxStars) ] ] Puts(out, $*N) count!0, count!1 = hp!1, hp!0 printleft(out, imag, imsym, sym eq lastSym, count) lastSym = sym DoubleAdd(cum, count) ] printpercent(out, cum, total) printstars(out, count, perStar, maxStars) Puts(out, $*N) ] and findsym(imag) = valof [ let imsym = imag while isymap!imsym eq 0 do imsym = imsym-1 resultis imsym ] and callprint(out, hist) be [ Wss(out, "*014Call instructions:*N") // ↑L, new page special = special1c let cfrom, cto = nCalls, nCalls Dvec(callprint, lv cfrom, lv cto) let ncalls = 0 for imag = 0 to lHist-1 do [ let real = irmap!imag if real eq -1 loop let hp = hist+real*lHE if (hp!0 eq 0) & (hp!1 eq 0) loop let rdest = nia1(imag) let idest = rimap!rdest unless (idest ge 0) & isentry1(idest) loop let ptr = ncalls until ptr eq 0 do [ let nptr = ptr-1 if cto!nptr le idest break cfrom!ptr, cto!ptr = cfrom!nptr, cto!nptr ptr = nptr ] cfrom!ptr, cto!ptr = imag, idest ncalls = ncalls+1 ] let count = vec 2 let lastto = -1 for j = 0 to ncalls-1 do [ if cto!j ne lastto then [ lastto = cto!j let hp = hist+irmap!lastto*lHE count!0, count!1 = hp!1, hp!0 PutTemplate(out, "<$S> $ED*N", isymap!lastto, count) ] let imag = cfrom!j let hp = hist+irmap!imag*lHE count!0, count!1 = hp!1, hp!0 Wss(out, " ") printleft(out, imag, findsym(imag), false, count) Puts(out, $*N) ] ] and printleft(out, imag, imsym, space, count) be // Print the symbol (or blanks if space), +offset, special stuff, count [ let sym = isymap!imsym test space ifso for i = 1 to sym>>BS.length do Puts(out, $*S) ifnot Wss(out, sym) let d = imag-imsym if d ne 0 then PutTemplate(out, "+$D", d) let width = sym>>BS.length+(d eq 0? 0, d ls 10? 2, d ls 100? 3, d ls 1000? 4, 5) width = width+special(out, imag, irmap!imag) // Do machine-dependent stuff let nd = ndigits(count) if width+nd+2 gr lLabel then [ Puts(out, $*N); width = 0 ] for j = width to lLabel-nd-2 do Puts(out, $*S) PutTemplate(out, "$ED ", count) ] and ndigits(v) = valof // Return the number of digits in the decimal representation of v [ if v!0 ne 0 then [ let w, r = vec 2, vec 2 DPDIVIDE(v, table[ 0; 10000], w, r) resultis ndigits(w)+4 ] let n = v!1 resultis (n ls 0? 5, n ls 10? 1, n ls 100? 2, n ls 1000? 3, n ls 10000? 4, 5) ] and printstars(out, count, perStar, max) = valof // Returns # of characters printed [ let c, d = vec 2, vec 2 c!0, c!1 = count!0, count!1 d!0, d!1 = perStar!0 rshift 1, (perStar!0 lshift 15) + (perStar!1 rshift 1) DoubleAdd(c, d) // round numerator let q, r = vec 2, vec 2 DPDIVIDE(c, perStar, q, r) let stars = q!1; if stars eq 0 then stars = 1 let nchars = stars if stars gr max then [ PutTemplate(out, "($3D)", stars) stars, nchars = max-5, max ] for j = 1 to stars do Puts(out, $**) resultis nchars ] and printpercent(out, cum, total) be [ let per = per10000(cum, total) PutTemplate(out, "($3D.$2F0D) ", per/100, per rem 100) ] and per10000(num, denom) = valof // Very carefully compute num*10000/denom // Know both are positive and num le denom [ let r1 = vec 2 let q1 = per100(num, denom, r1) let r2 = vec 2 let q2 = per100(r1, denom, r2) resultis q1*100+q2 ] and per100(num, denom, remn) = valof // Return the quotient of num*100/denom, leave the remainder in remn // Know both num and denom are positive, and num le denom [ let n, d = vec 2, vec 2 n!0, n!1 = num!0, num!1 d!0, d!1 = denom!0, denom!1 while n!0 gr 327 do // make sure 100* will fit [ n!1 = (n!0 lshift 15) + (n!1 rshift 1) d!1 = (d!0 lshift 15) + (d!1 rshift 1) n!0, d!0 = n!0 rshift 1, d!0 rshift 1 ] let n100 = vec 2 mul100(n, n100) let p = vec 2 DPDIVIDE(n100, d, p, remn) resultis p!1 ] and mul100(v, w) be // Multiply a double-precision number by 100 // Know the result will fit [ let Multiply = table[ 175400b // inc 3 3 // skip arg count word 2343b // jmp @343b // Bcpl runtime, leaves AC0 = hi part ] w!0 = v!0*100 + Multiply(v!1, 100) // What a kludge! w!1 = v!1*100 // discards hi part ] and special0(out, imag, real) = valof // Special stuff for Dolphin (opcode entries, calls) [ let width = 0 if (real&6003b) eq 2001b then // opcode entry [ PutTemplate(out, " [$3F0O]", (real rshift 2) & 377b) width = width+6 ] // If the instruction preceding imag is a Call, // return the imaginary address being called, otherwise -1 let idest = valof [ let ip = idata+(imag-1)*lMI if ip>>MI0.jc ne jcCall0 resultis -1 let ja = nia0(imag-1) let page = irmap!(imag-1) rshift 8 ip = ip-lMI if (ip>>MI0.meminst eq 0) & (ip>>MI0.f1 eq f1LoadPage0) then page = ip>>MI0.f2 resultis rimap!((page lshift 8) + ja) ] if (idest ne -1) & (isymap!idest ne 0) then // note the call [ let dsym = isymap!idest PutTemplate(out, " <$S>", dsym) width = width+dsym>>BS.length+3 ] resultis width ] and nia0(imag) = valof [ let ip = idata+imag*lMI resultis (ip>>MI0.jahi2 lshift 6) + ip>>MI0.jalo6 ] and special1(out, imag, real) = valof // Special stuff for Dorado (flag subroutine entries) [ let w = 0 let rdest = nia1(imag) let idest = rimap!rdest if (idest ge 0) & isentry1(idest) then [ let s = isymap!idest // must be exact PutTemplate(out, " <$S>", s) w = w + s>>BS.length + 3 nCalls = nCalls+1 ] resultis special1c(out, imag, real) + w ] and special1c(out, imag, real) = valof // Replaces special1 during listing of calls [ if isentry1(imag) then [ let s = ((real&77b) eq 0? " (****)", " (**)") Wss(out, s) resultis s>>BS.length ] resultis 0 ] and isentry1(imag) = valof // Return true if imag is a subroutine entry [ let real = irmap!imag if (real&17b) ne 0 resultis false // not an entry if isymap!imag eq 0 resultis false // no label, almost certainly not an entry if nia1(imag-1) eq real resultis false // previous instruction falls through to here, almost certainly not an entry if (ifue!(real rshift 6) & oneBits!((real rshift 2)&17b)) ne 0 resultis false // an IFU entry resultis true ] and nia1(imag) = valof // Return the target address from JCN [ let jfrom = irmap!imag let ip = idata+imag*lMI let jnlo = ip>>MI1.jnlo if ip>>MI1.jclong eq jcLong1 then // must test long before fast resultis (ip>>MI1.ff lshift 4) + (ip>>MI1.jnlongx4 lshift 2) + jnlo if ip>>MI1.jclocal eq jcLocal1 then resultis (jfrom & 7700b) + (ip>>MI1.jnlocalx4 lshift 2) + jnlo if ip>>MI1.jcglobal eq jcGlobal1 then resultis ((ip>>MI1.jnglobalx4 lshift 2) + jnlo) lshift 6 // if ip>>MI1.jcfast eq jcFast1 then if (ip>>MI1.jcondx4 lshift 2 + ip>>MI1.jcondlo) ne 7 then resultis (jfrom & 7700b) + (ip>>MI1.jnfastx8 lshift 3) + (ip>>MI1.jnfastx2 lshift 1) resultis (jfrom & 7700b) + ((jfrom+1) & 77b) // a Return or other exception ] and pmemproc(memx, width, name) = valof [ test (name!0 eq 1000b+$I) & (name!1 eq $M*400b) ifso [ imx = memx; resultis pimproc ] ifnot test (name!0 eq 2000b+$I) & (name!1 eq $F*400b+$U) & (name!2 eq $M*400b) ifso [ machine = 1; resultis pifumproc ] ifnot resultis Noop ] and pimproc(imag, data) be [ let real = data!3 & 7777b rimap!real = imag irmap!imag = real MoveBlock(idata+imag*lMI, data, lMI) ] and pifumproc(addr, data) be [ let real = IFADr2mask-data>>IFUM.notIFADr2 let ifup = ifue+(real rshift 4) @ifup = oneBits!(real&17b) % @ifup // mark entry ] and psymproc(memx, value, name) be // Save symbolic address if memx eq imx then [ let nw = name>>BS.length/2+1 let sym = Allocate(mbzone, nw) MoveBlock(sym, name, nw) isymap!value = sym ]