      TITLE   PPE package
;       CALL PPESTART(L1,L2,NBIN) to start the analysis between addresses L1 
;                                 and L2 (relative to the main program).
;                                 If L1=L2=0, the whole of core will be
;                                 analysed. NBIN is the number of bins in the
;                                 histogram (if NBIN=0, 256 is assumed).
;       CALL PPESTOP('dest') to stop the analysis & print the results on 'dest'
;                            normally dest will be 'lp:' or 'vdu:'
      MODULE  F77_PPE
      EXPORTC PPESTART,PPESTOP
      IMPORTC Allocate,Deallocate
      IMPORTC FindOutput,XOutputStream,XSWriteByte,XSBlockWrite,CloseStream
      IMPORTC XDeclareEventHandler,XSetEventStatus,XRemoveEventHandler
      IMPORTC F_MAIN
      AREADEF PROG,[CODE,PIC,READ,SHARED],WORD
      AREADEF DATA,[PIC],DOUBLE
      DEFSB   DATA
      AREA    PROG
;       interrupt code here
      ADDQD   1,total     ;increment total counts
      MOVD    4(24(SP)),R0;PC at interrupt
      SUBD    low,R0
      BCS     below       ;below histogram
      MOVQD   0,R1
      DEID    width,R0    ;get bin number (DEI is faster than DIV)
      CMPD    nbins,R1
      BLE     above       ;above histogram
add   ADDQW   1,0(hist)[R1:W]  ;fill histogram
      BCS     extra       ;overflow bin contents
      RXP     20
below ADDQD   1,under     ;below histogram
      RXP     20
above ADDQD   1,over      ;above histogram
      RXP     20
extra MOVQW   -1,0(hist)[R1:W] ;contents overflow - fill with maximum
      RXP     20
;
PPESTART;    initialise the package
      SPRD    FP,TOS      ;save FP
      LPRD    FP,12(SP)   ;address of argument list in FP
;      MOVZWD  mmain,R3    ;MOD table address of F_MAIN
      LXPD    F_MAIN,R3   ;get code descriptor of main entry point
      MOVZWD  R3,R3       ;extract module table address of main program
      MOVD    8(R3),R3    ;get load address of main program
      MOVD    R3,main     ;store load address in main
      MOVD    =#X100000,R2;size of memory on this machine
      MOVD    0(0(FP)),R0 ;lower bound in memory
      ADDD    R3,R0       ;bias to main program
      MOVD    0(4(FP)),R1 ;upper bound in memory
      ADDD    R3,R1       ;bias to main program
      CMPD    R0,R1       ;check lower < upper
      BHS     pt1         ;no, so take defaults
      CMPD    R1,R2       ;check upper <= size of memory
      BLS     pt2         ;OK
pt1   MOVQD   0,R0        ;set default low to bottom of memory
      MOVD    R2,R1       ;set default top to top of memory
pt2   MOVD    R0,low      ;store low end of histogram
      MOVD    0(8(FP)),R2 ;number of bins
      CMPQD   0,R2        ;check it is > 0
      BLT     pt3
      ADDR    @256,R2     ;default 256 bins
pt3   MOVD    R2,nbins    ;store number of bins
      LPRD    FP,TOS      ;restore FP
      SUBD    R0,R1       ;width of whole histogram
      ADDD    R2,R1
      ADDQD   -1,R1       ;allow for rounding
      DIVD    R2,R1       ;divide by number of bins
      MOVD    R1,width    ;store bin width
      ADDD    R2,R2       ;2 * nbins
      MOVD    R2,TOS      ;size in bytes
      ADDR    hist,TOS
      CXP     Allocate    ;allocate space for histogram
      CMPQD   0,R0
      BGT     nosp        ;no space available
      MOVD    nbins,R0
pt4   MOVQW   0,-2(hist)[R0:W]
      ACBD    -1,R0,pt4   ;clear histogram
      MOVQD   0,total     ;clear total counts
      MOVQD   0,under     ;clear underflow bin
      MOVQD   0,over      ;clear overflow bin
;       now set up the interrupts every TV scan
      MOVQD   -4,TOS      ;handle
      MOVQD   0,TOS       ;call after all other event processors
      MOVQD   4,TOS       ;TV scan events
      SPRD    MOD,TOS     ;descriptor for interrupt code
      CXP     XDeclareEventHandler
      MOVQD   1,TOS       ;to enable events
      MOVQD   4,TOS       ;TV scan events
      CXP     XSetEventStatus
      MOVD    R0,stat     ;save previous status
      RXP     4           ;return
nosp  MOVQD   -2,hist     ;no space for histogram (flag with hist =-2)
      RXP     4           ;return
;
PPESTOP   ;stop PPE and print results
      CMPQD   -1,hist
      BGE     pp3         ;null run
;       stop PPE
      MOVD    stat,TOS    ;old status of event
      MOVQD   4,TOS       ;TV scan events
      CXP     XSetEventStatus
      MOVQD   -4,TOS      ;handle
      MOVQD   4,TOS       ;TV scan events
      SPRD    MOD,TOS     ;descriptor for interrupt code
      CXP     XRemoveEventHandler
;       find maximum bin contents and calculate scale factor for plot
      MOVQD   0,R0        ;maximum so far
      MOVD    nbins,R1    ;bin count
pp1   CMPW    -2(hist)[R1:W],R0 ;compare contents with maximum
      BLS     pp2         ;contents .LE. maximum
      MOVW    -2(hist)[R1:W],R0 ;update maximum
pp2   ACBD    -1,R1,pp1   ;loop over bins
      ADDD    =65,R0
      DIVD    =66,R0      ;number of hits per * on the plot
      MOVW    R0,scale    ;store bin scaling factor
;       now print results
pp3   MOVD    0(8(SP)),R0 ;address of 'file' descriptor
      MOVD    4(R0),TOS   ;length of file name
      MOVD    0(R0),TOS   ;address of file name
      CXP     FindOutput
      CMPQD   0,R0
      BLE     pp4
      CXP     XOutputStream;can't open output file, so use standard output
pp4   MOVD    R0,strm     ;save output stream number
      CMPQD   -1,hist     ;check histogram exists
      BGE     pp9         ;no, so skip printing
      SUBD    main,low    ;adjust low to start at main program
      ADDR    tit+1,R0    ;print "Program histogram results"/"_______"
      BSR     prnt
      ADDR    mprg+1,R0   ;print /"F_MAIN at"
      BSR     prnt
      MOVD    main,R2     ;print main
      ADDR    @13,R0      ;13 digits
      BSR     prhex       ;in hex
      ADDR    relat+1,R0  ;print " (Hex); histogram addresses are"....
      BSR     prnt
      ADDR    rang+1,R0   ;print /"Histogram from"
      BSR     prnt
      MOVD    low,R2      ;print low
      ADDR    @8,R0       ;8 digits
      BSR     prhex       ;in hex
      ADDR    to+1,R0     ;print " (Hex)"/"        to"
      BSR     prnt
      MOVD    nbins,R2
      MULD    width,R2
      ADDD    low,R2      ;print width*nbins+low
      ADDR    @10,R0      ;10 digits
      BSR     prhex       ;in hex
      ADDR    bsiz+1,R0   ;print " (Hex)"/"Bin size"
      BSR     prnt
      MOVD    width,R2    ;print width
      ADDR    @14,R0      ;14 digits
      BSR     prhex       ;in hex
      ADDR    nobin+1,R0  ;print " (Hex)"/"Number of bins"
      BSR     prnt
      MOVD    nbins,R2    ;print nbins
      ADDR    @8,R0       ;8 digits
      BSR     prdec       ;in decimal
      ADDR    totc+1,R0   ;print /"Total sample"
      BSR     prnt
      MOVD    total,R2    ;print total
      ADDR    @10,R0      ;10 digits
      BSR     prdec       ;in decimal
      ADDR    ufl+1,R0    ;print /"Underflows"
      BSR     prnt
      MOVD    under,R2    ;print under
      ADDR    @12,R0      ;12 digits
      BSR     prdec       ;in decimal
      ADDR    binc+1,R0   ;print /"Bin contents"
      BSR     prnt
      MOVD    total,R2
      SUBD    under,R2
      SUBD    over,R2     ;print total-under-over
      ADDR    @10,R0      ;10 digits
      BSR     prdec       ;in decimal
      ADDR    ofl+1,R0    ;print /"Overflows"
      BSR     prnt
      MOVD    over,R2     ;print over
      ADDR    @13,R0      ;13 digits
      BSR     prdec       ;in decimal
      ADDR    mult+1,R0   ;print /"Bin scaling factor"
      BSR     prnt
      MOVZWD  scale,R2    ;print scale
      MOVQD   4,R0        ;4 digits
      BSR     prdec       ;in decimal
      ADDR    this+1,R0   ;print /"_______"/"Addr. Content Histogram"
      BSR     prnt
;       now print bin contents
      MOVD    hist,TOS    ;save histogram address for deallocation
      MOVB    =' ',skip   ;no bins skipped so far -> skip = blank
pp5   CMPQW   0,0(hist)   ;check bin contents
      BEQ     pps         ;empty bin so skip
      MOVD    low,R2      ;print low
      MOVQD   5,R0        ;5 digits
      BSR     prhex       ;in hex
      MOVZWD  0(hist),R2  ;print bin contents
      MOVQD   7,R0        ;7 digits
      BSR     prdec       ;in decimal
      MOVZBD  skip,TOS    ;print skip flag
      MOVD    strm,TOS
      CXP     XSWriteByte
      ADDR    @':',TOS    ;print ":"
      BR      pp7
pp6   ADDR    @'**',TOS   ;make bar chart with *'s
pp7   MOVD    strm,TOS
      CXP     XSWriteByte
      SUBW    scale,0(hist)
      BCC     pp6         ;loop over *'s to print
      ADDR    @10,TOS     ;print newline
      MOVD    strm,TOS
      CXP     XSWriteByte
      MOVB    =' ',skip   ;no bins skipped 
pp8   ADDD    width,low   ;update low edge of histogram bin
      ADDQD   2,hist      ;update histogram bin address
      ACBD    -1,nbins,pp5;loop over bins
      CXP     Deallocate  ;release histogram space
close MOVD    strm,TOS    ;close print stream
      CXP     CloseStream
      MOVQD   -1,hist     ;reinitialize for a new run
      RXP     4           ;return
pp9   ADDR    nsp+1,R0    ;print "No space available for PPE histogram"
      BNE     ppa         ;unless hist = -1 when...
      ADDR    nit+1,R0    ;print "PPE package not initialized"
ppa   BSR     prnt
      BR      close
pps   MOVB    ='+',skip   ;flag skipped bins with +
      BR      pp8
;       print in hex: R0 digits (blank fill), data in R2
prhex ADDR    @16,R1      ;base 16 for hex
      BR      prnum
;       print in decimal: R0 digits (blank fill), data in R2
prdec ADDR    @10,R1      ;base 10 for decimal
;       print number in R2 to base R1, length R0 with blank fill
;       it is assumed  that R2 >= 0,  0 < R1 < 39, & 0 < R0 < 16
prnum MOVB    R0,ndig     ;preserve count
      BR      pn2         ;always print one digit
pn1   CMPQD   0,R2
      BEQ     pn3         ;no more digits
pn2   MOVQD   0,R3        ;clear more significant part of numerator
      DEID    R1,R2       ;find next digit
      ADDB    ='0',R2     ;make into ASCII
      CMPB    R2,='9'
      BLE     pn4         ;numeric digit
      ADDQB   7,R2        ;alphabetic digit
      BR      pn4
pn3   MOVB    =' ',R2     ;blank for null digit
pn4   MOVB    R2,digit-1[R0:B] ;store digit
      MOVD    R3,R2       ;set next numerator
      ACBD    -1,R0,pn1   ;loop over digits
      ADDR    digit,R0
;       print message at R0, length stored in -1(R0) {as in DCS  "xx"}
prnt  MOVD    R0,TOS      ;address of message
      MOVZBD  -1(R0),TOS  ;length of message
      MOVD    strm,TOS    ;print stream
      CXP     XSBlockWrite
      RET     0
;         formats
tit   DCS     "Program histogram results*N-------------------------"
mprg  DCS     "*NF_MAIN at"                   ;13 digits
relat DCS     " (Hex); histogram addresses are relative to this"
rang  DCS     "*NHistogram from"              ;8 digits
to    DCS     " (Hex)*N          to"          ;10 digits
bsiz  DCS     " (Hex)*NBin size"              ;14 digits
nobin DCS     " (Hex)*NNumber of bins"        ;8 digits
totc  DCS     "*NTotal sample"                ;10 digits
ufl   DCS     "*NUnderflows"                  ;12 digits
binc  DCS     "*NBin contents"                ;10 digits
ofl   DCS     "*NOverflows"                   ;13 digits
mult  DCS     "*NBin scaling factor"          ;4 digits
this  DCS     "*N---------------------------*NAddr. Content Histogram*N"
nsp   DCS     "No space available for PPE histogram*N"
nit   DCS     "PPE package not initialized*N"
;
      AREA    DATA
hist  DCD     -1          ;address of stored histogram (initially -1)
;mmain CDESC   F_MAIN      ;code descriptor of main program.
main  ALLOCD  1           ;address of main program
low   ALLOCD  1           ;low address to be analysed
width ALLOCD  1           ;bin width in bytes
nbins ALLOCD  1           ;number of bins
stat  ALLOCD  1           ;previous status of TV scan event
under ALLOCD  1           ;underflow bin
over  ALLOCD  1           ;overflow bin
total ALLOCD  1           ;total contents
strm  EQU     stat        ;stream number for output
scale ALLOCW  1           ;number of hits per * on plot
ndig  ALLOCB  1           ;number of digits to print
digit ALLOCB  15          ;digits for printing
skip  EQU     under       ;flag for skipped bins
      END
