;
;       TITLE   DU
;
;       Disk usage by PPn for a device
;
; THIS PROGRAM MAY BE FREELY DISTRIBUTED TO AMUS MEMBERS
; COURTESY OF WHITE HOUSE SOFTWARE, INC.
;
; Note: to get a sorted listing of disk usage, do the following
;       DU X.LST=DSK2:
;       SORT X.LST
;       record length = 80
;       key size      = 11
;       key position  = 11
;       order         = d  (descending)
;       Press RETURN on next key size question
;
;Edit History:
;
; 5/30/86 1.0(0) - David Greene
;       Written
;
VEDIT=0.

       SEARCH  SYS
       SEARCH  SYSSYM


; Define TAB and Carriage Return
TAB=9.
CR=13.

; Define Version Information
VMAJOR=1.
VMINOR=0.
VSUB=0.
VWHO=0.


; Start of program
PHEAD:  PHDR    -1,0,PH$REE!PH$REU

       TYPECR  <DU is courtesy of White House Software, Inc.>

       BYP                             ; skip blanks and tabs
       LIN                             ; end of string?
       JNE     DU10
DU0:
       TYPECR  <Usage: DU Dev:         - Print listing to screen>
       TYPECR  <       DU =Dev:        - Print listing to DU.LST>
       TYPECR  <       DU outfil=Dev:  - Pring listing to specified output file>
       TYPECR  <                         (Default extention is .LST)>
       CRLF
       EXIT
;
; get command string file specifications
;
DU10:
       GETIMP  IMPSIZ,A5               ; get impure area
       CLR     TOTCNT(A5)              ; no kilobytes yet
       SETB    FILOUT(A5)              ; assume output is to a file
       CMPB    @A2,#'=                 ; output to file.LST?
       BEQ     DU30                    ; yup
       MOV     A2,D4                   ; save A2 for a second
       FSPEC   DDBOUT(A5),LST          ; output file specification
       BYP                             ; ignore spaces
       CMPB    @A2,#'=                 ; was that the output file?
       BEQ     DU20                    ; yup
       MOV     D4,A2                   ; ->start of input file name
       CLRB    FILOUT(A5)              ; output is not to a file
       BR      DU80

; DU file.out=Dev:
; (full input and output specification)
DU20:
       INC     A2                      ; bypass '='
       BYP                             ; any device spec?
       LIN                             ;  no - show user how to invoke
       JEQ     DU0
       BR      DU80                    ; done with file specifications


; DU =Dev:
; (output is to file.LST)
DU30:
       INC     A2                      ; bypass '='
       BYP                             ;
       LIN                             ; any device specification?
       JEQ     DU0                     ;  no - show user how to invoke
       MOV     A2,D4                   ; ->start of input file name
       PUSH    A2                      ; save A2 a second
       LEA     A2,OPNFIL               ; index default output filename
       FSPEC   DDBOUT(A5),LST          ; DU.LST
       POP     A2
       BR      DU80

; Default filename when .DU =Dev#:
OPNFIL:
       ASCII   /DU/
       BYTE    0
       EVEN

DU80:
;
; set up input device
;
       CLRW    DDBIN(A5)               ; not yet INITed
       INIT    DDBIN(A5)               ; get buffers
       SET     D6                      ; stop scanning after device spec
       FSPEC   DDBIN(A5)               ; process device spec

;
; open output file
;
       TSTB    FILOUT(A5)              ; output to a file?
       BEQ     MAIN                    ; nope
       CLRW    DDBOUT(A5)              ; not yet INITed
       INIT    DDBOUT(A5)              ; get buffers
       LOOKUP  DDBOUT(A5)              ; look it up
       BNE     40$
       DSKDEL  DDBOUT(A5)              ; delete existing file
40$:
       OPENO   DDBOUT(A5)              ; open for output
; output header for file
       LEA     A2,DDBOUT(A5)           ; A2 must point to output DDB
       OUTS    OT$DDB,<Disk Usage listing for >
       OFILE   DDBIN(A5),OT$DDB
       OUTCR   OT$DDB


PAGE
;*************
;*   MAIN    *
;*************
; Main processing loop
; We go from PPn to PPn and call DOPPN which calculate disk usage
; for that account.
MAIN:
; MFD starts with block number 1
       MOV     #1,DDBIN+D.REC(A5)      ; block number 1

; Main MFD loop
5$:
       READ    DDBIN(A5)               ; read MFD
       MOV     DDBIN+D.BUF(A5),A2      ; A2->MFD buffer
10$:
       TSTW    MF.PPN(A2)              ; end of MFD?
       BEQ     20$                     ;  Yes

; process a PPn
       CTRLC   EXIT$                   ; allow CTRL-C
       CALL    DOPPN                   ; process one PPn

; get next PPn for loop
       ADD     #MF.SIZ,A2              ; Advance to next MFD entry
       MOV     DDBIN+D.BUF(A5),A6      ; End of buffer?
       ADD     DDBIN+D.SIZ(A5),A6
       SUB     #MF.SIZ,A6
       CMP     A2,A6                   ; are we at the end of an MFD block?
       BLO     10$                     ;  no - continue

; we are past the last PPn in this block, get the next one
; if there is one
       MOVW    MF.NXT(A6),DDBIN+D.REC+2(A5);Advance to next MFD record
       BNE     5$                      ;   And continue unless no more blocks

; no more PPns
; output total kilobytes used
20$:
       LEA     A2,OUTLIN(A5)
       OUTSP   OT$MEM,<Total disk space used is>
       MOV     TOTCNT(A5),D4
       ROR     D4,#1                   ; divide by 2; N bit set if remainder
       CLR     D1
       MOVW    D4,D1                   ; get number of kilobytes

; output kilobytes used
       DCVT    0,OT$MEM                ; output number of Kilobytes
       MOVB    #'.,(A2)+               ; output decimal point
       MOVB    #'0,D1                  ; no remainder yet
       TST     D4                      ; any remaider from before?
       BPL     25$                     ;  no - finish
       MOVB    #'5,D1                  ; .5 remainder

; output remainder
25$:
       MOVB    D1,(A2)+                ;  yes - show user
       OUTS    OT$MEM,<K bytes>        ; show number is kilobytes
       MOVB    #CR,(A2)+               ; output Carriage return
       CLRB    @A2                     ; we need null terminator

; print line to appropriate place
       CALL    LINOUT                  ; output last line

; close output file if open
       TSTB    FILOUT(A5)              ; are we outputing to a file?
       BEQ     EXIT$                   ;  no - no need to close
       CLOSE   DDBOUT(A5)              ;  yes - wrap it up
EXIT$:
       EXIT                            ; we're done


PAGE
;*************
;*  DOPPN    *
;*************
; Entry:
;       A2->PPN @ MFD
; Exit:
;       A1,A3,A4,D1,D4 modified
DOPPN:
; we have a PPn match
       PUSH    A2                      ; save MFD pointer
       PUSH    D.REC+DDBIN(A5)         ; save MFD block number
       PUSH    D.BUF+DDBIN(A5)         ; save buffer address

; Use a seperate buffer for the directory
       LEA     A6,DIRBUF(A5)           ; don't use any registers
       MOV     A6,D.BUF+DDBIN(A5)      ; set buffer for directory to use

; output PPn
       MOV     A2,A3                   ;  yes - save PPn pointer

; set octal
       JOBIDX  A4                      ; get my job
       PUSHW   JOBTYP(A4)              ; save my type
       ANDW    #^C<J.HEX>,JOBTYP(A4)   ; make sure we are octal

; output PPn to output line buffer
       LEA     A2,OUTLIN(A5)           ; A2-> output buffer
       CLR     D1
       MOVB    #'[,(A2)+
       MOVB    1(A3),D1                ; get project number
       OCVT    0,OT$MEM                ; output it
       MOVB    #',,(A2)+
       MOVB    @A3,D1                  ; programmer number
       OCVT    0,OT$MEM                ; output it
       MOVB    #'],(A2)+
       MOVB    #40,(A2)+               ; followed by some blanks
       MOVB    #40,(A2)+
       MOVB    #40,(A2)+
       MOVB    #40,(A2)+
       MOVB    #40,(A2)+

; restore jobtyp
       POPW    JOBTYP(A4)              ; restore type
       MOV     A3,A2                   ; restore pointer

; set directory info and get space used in this UFD
       MOVW    2(A2),D.REC+2+DDBIN(A5) ; set directory block number

; SCNDIR returns D4 with number of blocks in directory
       CALL    SCNDIR                  ; scan directory (D4)

; Now D4 contains number of blocks. After we rotate, (which will divide the
; number of blocks by 2, giving us kilobytes,) we will have the N-bit set if
; set if we have an extra half a kilobyte

; first accumulate total
       ADD     D4,TOTCNT(A5)           ; keep running total

; now calculate kilobytes
       ROR     D4,#1                   ; divide by 2; N bit set if remainder
       CLR     D1
       MOVW    D4,D1                   ; get number of kilobytes

; output kilobytes used
       PUSH    A2                      ; save pointer
       LEA     A2,10.+OUTLIN(A5)       ; allow exactly 10 spaces for [PPN]
       DCVT    5,OT$MEM!OT$ZER         ; output number of Kilobytes
       MOVB    #'.,(A2)+               ; output decimal point
       MOVB    #'0,D1                  ; no remainder yet
       TST     D4                      ; any remainder from before?
       BPL     25$                     ;  no - finish
       MOV     #'5,D1                  ; .5 remainder

; output remainder
25$:
       MOVB    D1,(A2)+                ;  yes - show user
       MOVB    #'K,(A2)+               ; show number is kilobytes
       MOVB    #CR,(A2)+               ; output Carriage return
       CLRB    @A2                     ; place at end of output buffer
       POP     A2

; Now print line
       CALL    LINOUT                  ; output line to appropriate place

; Done with this PPn - restore info
100$:
       POP     D.BUF+DDBIN(A5)         ; restore MFD areas
       POP     D.REC+DDBIN(A5)
       POP     A2
       RTN


PAGE
;*************
;*  SCNDIR   *
;*************
; Entry:
;       A2->PPn buffer[not used]
;       DDBIN set up for directory reads
;
; Exit:
;       D4 contains number of total blocks in this directory
;
SCNDIR:
       SAVE    A2,D1,D3                ; save work registers
       CLR     D4                      ; clear block count
       TST     D.REC+DDBIN(A5)         ; is there a directory ?
       JEQ     RTNDIR                  ;  no - just return

; This is the master loop
DIRLOP:
       READ    DDBIN(A5)               ; get a directory block
       MOV     D.BUF+DDBIN(A5),A2      ; A2 -> directory buffer
       MOVW    (A2)+,D1                ; D1 -> next block number
       MOV     #42.,D3                 ; 42 entries per block

; This loop scans an entire block and checks for matches
DIRLP2:
       TSTW    @A2                     ; are we at the end of the directory?
       BEQ     RTNDIR                  ;  yes - return
       CMPW    @A2,#-1                 ; empty slot ?
       BEQ     NXTDIR                  ;  yes - get the next one
       ADDW    6(A2),D4

; get the next directory item, if end of block
; then set up for next block
NXTDIR:
       ADD     #14,A2                  ; next directory item please
       SOB     D3,DIRLP2               ; are we at the end of the block ?
       MOVW    D1,D.REC+2+DDBIN(A5)    ;  yes - set next block number
       BNE     DIRLOP                  ;  if zero we're done

RTNDIR:
       REST    A2,D1,D3
       RTN


PAGE
;*************
;*  LINOUT   *
;*************
;Output OUTLIN(A5) to terminal or file
; Entry:
;       FILOUT(A5) set if we want to output to file
;                  clear if we want output to terminal
; Exit:
;       Line outputted
LINOUT:
       PUSH    A2
       TSTB    FILOUT(A5)              ; are we outputting to a file ?
       BEQ     10$                     ;  no - output to screen

; output line to file
       LEA     A2,DDBOUT(A5)           ; A2->output DDB for OUTL call
       OUTL    OUTLIN(A5),OT$DDB       ; output line to file
       BR      20$

; output line to terminal
10$:
       TTYL    OUTLIN(A5)

; finish routine
20$:
       POP     A2
       RTN


;define impure area
SAVDOT=.
=0
DDBIN:  BLKB    D.DDB                   ; input ddb
DDBOUT: BLKB    D.DDB                   ; output ddb
DIRBUF: BLKB    512.                    ; disk buffer for UFD reads
OUTLIN: BLKB    40.                     ; output file buffer
TOTCNT: BLKL    1                       ; total kilobytes used
FILOUT: BLKB    1                       ; output flag
       EVEN
IMPSIZ=.
=SAVDOT

       END