;--------------------------------------------------------------------- ; ; PPNUSE.M68 - display summary of files in AMOS PPNs ; ;--------------------------------------------------------------------- ; =============== BEGINNING OF COPYRIGHT NOTICE ================ ; PPNUSE.M68 is Copyright (c) Applied Systems Research, Dec 1992 ; Permission is given to copy this program freely, but this ; copyright notice must be included and remain unchanged. ; You may not charge anyone for this program. ; ================== END OF COPYRIGHT NOTICE =================== ; EXTENDED DIRECTORIES: ; This program works with extended directories. ; It does a little bit of everything (MFD,UFD,files). ; Thus, it is handy when coding other extended directory programs. ; At appropriate places below, bugs in AMOS and its docs are discussed. ; Edit history: ; (100) 1994-Nov-16 - created by Bob Fowler with /E:n/D/S/T options ; (101) 1994-Nov-18 - add RMS file stats ; (102) 1994-Nov-21 - add estimate of tape backup overhead for UFDs ; Usage documentation: ; (1) Directories included in summary: ; "PPNUSE" scans the user's login account ; "PPNUSE [ppp,nnn]" scans another account ; "PPNUSE devn:[ppp,nnn]" scans an account on device devn ; "PPNUSE devn:" scans all accounts on device devn (vs one account) ; (2) Switches broaden the scan range: ; /D forces the scan range to include the entire device ; /S forces the scan range to include all devices ; (3) Details displayed (from lowest to highest level): ; Extension summary: included only by /E, suppressed by /T ; Account summary: included except when suppressed by /T ; Device summary: included whenever an entire device is scanned ; System summary: included whenever all devices are scanned ; (4) Other notes: ; /E requires an internal table be allocated for totals. ; The table size defaults to 100 extensions. ; If there are more than 100 extensions in any account, the ; 100th extension and beyond are totaled together as "etc". ; To allow for up to n extensions, use "/E:n". ; This can be used to abbreviate (/E:4) or enlarge (/E:1000). ; The absolute program limit is currently /E:6553 (=65535/10). ; Register usage: ; A3 = base of extension subtotals table ; A4 = pointer within AMOS device table ; A5 = base of impure work area ; Possible revisions: ; --- allow disk device families like "DSK" ; --- allow PPN families like "ALL:[100,100]" ; --- optionally send output to print file ; --- sort extension subtotals by extension or # of blocks ; (currently displayed in order of first occurance) ; --- change how the "etc" extensions are consolidated? ; --- breakdown totals by random/sequential files? ; --- check for overflow in xxxSSF running totals COPY SYS SEARCH SYSSYM ; for PHDR symbols VMAJOR = 1 VMINOR = 0 VEDIT = 102. TABSIZ = 10. ; size of entries in extension table TABMAX = 65535./TABSIZ ; maximum table size TABCNT = 100. ; default table count PSECT .=0 PPNUSE: PHDR -1,0,PH$REU ! PH$REE ; no privileges, reentrant, reusable GETIMP MEMLTH,A5,PRGEND ; get (and clear) impure work area ; ===== PROCESS INPUT LINE ===== ; set default scan conditions MOV #1,DEVONE(A5) ; default to one device CLR DEVTOT(A5) ; default to PPN totals ; clear device/drive/PPN in DDB CLRW SCNDDB+D.DEV(A5) ; clear device CLRW SCNDDB+D.DRV(A5) ; clear drive CLRW SCNDDB+D.PPN(A5) ; clear PPN ; process optional device/drive/PPN from input line BYP ; bypass blanks and tabs LIN ; any inputs? BEQ 10$ ; no - use login defaults FSPEC SCNDDB(A5) ; process device/drive/PPN 10$: TSTW SCNDDB+D.DEV(A5) ; was there a device? BNE INPDEV ; yes - use that device ; use defaults for no device input JOBIDX A1 ; get base of user's JCB MOVW JOBDEV(A1),SCNDDB+D.DEV(A5) ; user login device MOVW JOBDRV(A1),SCNDDB+D.DRV(A5) ; user login drive MOVW SCNDDB+D.PPN(A5),PPNONE(A5) ; was there a PPN? BNE GOTDEV ; yes - use it JOBIDX A1 ; get base of user's JCB MOVW JOBUSR(A1),PPNONE(A5) ; user login PPN BR GOTDEV ; device was input INPDEV: CMPW SCNDDB+D.DRV(A5),#-1 ; was there a drive number? BNE 10$ ; yes - use it ; if a device family (eg, "DSKn") scan is implemented, do it here ; for now, assume drive zero (eg, "DSK:" is treated as "DSK0:") CLRW SCNDDB+D.DRV(A5) ; clear drive to zero 10$: MOVW SCNDDB+D.PPN(A5),PPNONE(A5) ; was there a PPN? BNE 20$ ; yes - use it CLRW PPNONE(A5) ; all PPNs 20$: GOTDEV: ; switches CLR EXTMAX(A5) ; default to no extension subtotals GETOPT: BYP ; bypass blanks and tabs LIN ; anything left on line? JEQ GOTOPT ; no - finished with switches CMPB (A2)+,#'/ ; "/" ? JNE USAGE ; no - wrong syntax ; check for /E or /E:n CMPB @A2,#'E ; "E"? BNE NOTSWE ; no - try another MOV #TABCNT,EXTMAX(A5) ; default to 10 extension subtotals INC A2 ; go to next character CMPB @A2,#': ; ":"? BNE GETOPT ; no - use default value INC A2 ; go to next character GTDEC ; get number of extensions MOV D1,EXTMAX(A5) ; maximum extensions CMP D1,#TABMAX ; too large? BLOS GETOPT ; no - done TYPE MOV #TABMAX,D1 ; maximum table siz DCVT 0,OT$TRM ; display it TYPECR < extensions allowed> EXIT NOTSWE: ; check for /S CMPB @A2,#'S ; "S"? BNE NOTSWS ; no - try another INC A2 ; go to next character CLR DEVONE(A5) ; process all devices CLR PPNONE(A5) ; process all accounts on device BR GETOPT ; done NOTSWS: ; check for /D CMPB @A2,#'D ; "D"? BNE NOTSWD ; no - try another INC A2 ; go to next character CLR PPNONE(A5) ; process all accounts on device JMP GETOPT ; done NOTSWD: ; check for /T CMPB @A2,#'T ; "T"? BNE NOTSWT ; no - try another INC A2 ; go to next character MOV #1,DEVTOT(A5) ; device totals only JMP GETOPT ; done NOTSWT: ; process other switch options here ; no more switch options BR USAGE USAGE: TTYI ASCII |Usage: PPNUSE {devn:} {[ppp,nnn]} {/E{:n}} {/D} {/S} {/T}| BYTE 15,0 EVEN EXIT GOTOPT: ; If we are doing only one account, ignore /T TST PPNONE(A5) ; one account only? BEQ 10$ ; no - ok CLR DEVTOT(A5) ; yes - clear totals only flag 10$: ; ===== GET ROOM ===== ; reserve room for extension totals TST EXTMAX(A5) ; doing totals? BEQ GOTROM ; no - skip room MOV EXTMAX(A5),D0 ; extensions MUL D0,#TABSIZ ; x 10 ; the GETIMP MACRO apparently fails for values in registers Dn ; GETIMP D0,A3,PRGEND ; fails (generates a 0-byte memory module) ; instead, we must expand the MACRO ourselves PUSH D0 ; the above GETIMP generates "PUSH #0" here PUSH GETMEM @SP JNE PRGEND POP A3 POP GOTROM: ; ===== DEVICE LOOP ===== ; initialize grand totals CLR SYSBLX(A5) ; blocks in system CLR SYSFIL(A5) ; files in system CLR SYSPPN(A5) ; PPNs in system CLR SYSDEV(A5) ; devices in system CLR SYSSSF(A5) ; sum of squares of files on system ; Scan the (undocumented) AMOS device table ; Symbols used here are from disassembly of SYSSYM.UNV[7,7] TST DEVONE(A5) ; only one disk? BNE BEGDEV ; yes - bypass device loop setup MOV DEVTBL,A4 ; get base of device table NXTDEV: TST DV.BMP(A4) ; does this device have a bitmap? JEQ ENDDEV ; no - not a disk (could be CD Rom) MOVW DV.DEV(A4),SCNDDB+D.DEV(A5) ; put device into DDB MOVW DV.UNT(A4),SCNDDB+D.DRV(A5) ; put drive into DDB BEGDEV: INC SYSDEV(A5) ; one more device in system INIT SCNDDB(A5) ; get input buffer ORB #,SCNDDB+D.FLG(A5) ; display errors but return ; if filename is not cleared, then last filename of previous disk may appear ; on error messages relating to the new disk (eg, "not mounted") CLR SCNDDB+D.FIL(A5) ; clear filename CLRW SCNDDB+D.EXT(A5) ; clear extension DIRACC SCNDDB(A5),#DA$INI ; initialize search JNE RETDEV ; error ; initialize counts CLR DEVBLX(A5) ; blocks in device CLR DEVFIL(A5) ; files in device CLR DEVPPN(A5) ; PPNs on device CLR DEVSSF(A5) ; sum of squares of files on device ; ===== ACCOUNT LOOP ===== NXTPPN: DIRSCH SCNDDB(A5),#DS$DIR ; get next directory (PPN) TSTB SCNDDB+D.ERR(A5) ; error? (eg, disk not mounted) JNE RETDEV ; yes - quit device ; DSKANA uses following test for end of directory (documented): ; MOVW SCNDDB+D.DIR(A5),D0 ; result flags ; ANDW #DF$EOD,D0 ; last account? ; JNE FINDEV ; yes - finish device ; documentation example uses following test (without explanation): TSTW D6 ; last account? JMI FINDEV ; yes - finish device MOVW SCNDDB+D.FIL(A5),PPNCUR(A5) ; get PPN MOVW PPNONE(A5),D0 ; doing all accounts? BEQ 10$ ; yes - do this one CMPW D0,PPNCUR(A5) ; doing this account? BNE NXTPPN ; no - skip it 10$: ; The following three PUSHes (and their corresponding POPs later on) ; save and restore the 12-byte "Directory Marker" field. ; This is necessary in order to return to the previous directory level. ; Using FIX, it was determined that DSKANA uses this method. ; The Monitor Calls (Sep 1989) do not discuss how to do this. ; Indeed, the File Service System chapter only says this: ; "This [Directory Marker] field is for internal use and it ; is not expected that it will be accessed by user programs." ; Thanks guys. PUSH SCNDDB+D.DIR(A5) ; save to go back PUSH SCNDDB+D.DIR+4(A5) ; save PUSH SCNDDB+D.DIR+8.(A5) ; save ; drop one level in directory tree DIRACC SCNDDB(A5),#DA$NEW + DA$LVL ; drop a level ; initialize counts CLR PPNFIL(A5) ; files in account CLR PPNBLX(A5) ; blocks in account CLR EXTCUR(A5) ; clear extension table ; bump PPN counts INC SYSPPN(A5) ; one more PPN in system INC DEVPPN(A5) ; one more PPN in device ; ===== FILE LOOP ===== NXTFIL: CTRLC PRGEND DIRSCH SCNDDB(A5),#DS$DAT ; get next file ; DSKANA uses following test for end of directory (documented): ; MOVW SCNDDB+D.DIR(A5),D0 ; result flags ; ANDW #DF$EOD,D0 ; last file? ; BNE FINPPN ; yes - finish account ; documentation example uses following test (without explanation): TSTW D6 ; last file? BMI FINPPN ; finish account ; add this file to totals INC PPNFIL(A5) ; increment files in PPN INC DEVFIL(A5) ; increment files in device INC SYSFIL(A5) ; increment files in system MOV SCNDDB+D.FSZ(A5),D0 ; blocks in current file ADD D0,DEVBLX(A5) ; add to device blocks ADD D0,PPNBLX(A5) ; add to PPN blocks ADD D0,SYSBLX(A5) ; add to system blocks ; add this file to extension subtotals TST EXTMAX(A5) ; do we keep extension subtotals? BEQ NXTFIL ; no - skip it CLR D1 ; clear upper word to be safe MOVW SCNDDB+D.EXT(A5),D1 ; get file extension (RAD50) MOV A3,A0 ; start at base of table MOV EXTCUR(A5),D2 ; start search countdown BEQ FSTEXT ; if table empty, add new entry ; Local register usage: ; D0 (blocks in file) D1 (extension) D2 (countdown) A0 (table entry) NXTEXT: CMPW D1,@A0 ; match? BEQ OLDEXT ; yes - add to old entry DEC D2 ; end of table? BEQ NEWEXT ; yes - add new entry to table ADD #TABSIZ,A0 ; go to next entry BR NXTEXT ; check it NEWEXT: MOV EXTCUR(A5),D3 ; is table full? CMP D3,EXTMAX(A5) ; ... BLO ADDEXT ; no - add new entry MOVW #-1,@A0 ; flag miscellaneous extension BR OLDEXT ; add in file and blocks ADDEXT: ADD #TABSIZ,A0 ; create new entry FSTEXT: MOVW D1,@A0 ; put in new extension CLR 2(A0) ; clear file count CLR 6(A0) ; clear block count INC EXTCUR(A5) ; one more extension OLDEXT: ADD #1,2(A0) ; bump file count ADD D0,6(A0) ; add in blocks JMP NXTFIL ; go to next file ; ===== DISPLAY RESULTS FOR ONE ACCOUNT ===== FINPPN: TST DEVTOT(A5) ; totals only? JNE ENDPPN ; yes - bypass PPN totals ; the OT$OFP flag apparently does not work (does not display PPN) ; OFILE SCNDDB(A5),OT$TRM + OT$TSP + OT$OFD + OT$OFP OFILE SCNDDB(A5),OT$TRM + OT$TSP + OT$OFD ; "Then I'll do it myself", said the Little Red Hen. TYPE <[> PRPPN PPNCUR(A5) ; output current PPN TYPE <]> TYPE < has> ; only display extension totals if /E option selected TST EXTMAX(A5) ; do we have extension subtotals? BEQ 10$ ; no - skip it MOV EXTCUR(A5),D1 ; extensions in PPN DCVT 0,OT$TRM + OT$LSP + OT$TSP ; display it TYPE 10$: MOV PPNFIL(A5),D1 ; files in PPN DCVT 0,OT$TRM + OT$LSP + OT$TSP ; display it TYPE MOV PPNBLX(A5),D1 ; files in PPN DCVT 0,OT$TRM + OT$LSP + OT$TSP ; display it TYPECR ; display extension subtotals MOV EXTCUR(A5),D2 ; start extension countdown BEQ ENDPPN ; if table empty, skip display MOV A3,A0 ; start at base of table DSPLIN: MOV #3,D3 ; 3 extensions per display line DSPEXT: TYPE < *.> CMPW @A0,#-1 ; "miscellaneous" extension? BEQ DSPMSC ; yes - special output MOV A0,A1 ; RAD50 source LEA A2,EXTASC(A5) ; ASCII destination UNPACK ; unpack it CLRB @A2 ; end with null TTYL EXTASC(A5) ; ASCII destination BR DSPDON DSPMSC: TYPE DSPDON: MOV 2(A0),D1 ; display files in PPN ; DCVT 0,OT$TRM + OT$LSP + OT$TSP ; TYPE DCVT 6,OT$TRM + OT$ZER MOV 6(A0),D1 ; display blocks in PPN ; DCVT 0,OT$TRM + OT$LSP + OT$TSP ; TYPECR DCVT 7,OT$TRM + OT$ZER ADD #TABSIZ,A0 ; go to next table entry DEC D2 ; end of table? BNE DSPEOL ; no - keep going CRLF BR ENDPPN DSPEOL: DEC D3 ; end of line? BNE DSPEXT ; no - stay on same line CRLF ; finish this line BR DSPLIN ; start new line ENDPPN: ; add sum of squares of files into running device and system totals MOV PPNFIL(A5),D0 ; files in this PPN ; check for >2^16 in following? MUL D0,D0 ; square it ; check for >2^32 in following? ADD D0,DEVSSF(A5) ; add to device sum of squares ; check for >2^32 in following? ADD D0,SYSSSF(A5) ; add to system sum of squares ; restore markers to return to previous higher directory level POP SCNDDB+D.DIR+8.(A5) ; return to higher directory level POP SCNDDB+D.DIR+4(A5) POP SCNDDB+D.DIR(A5) JMP NXTPPN ; go to next account ; ===== DISPLAY RESULTS FOR ONE DEVICE ===== FINDEV: TSTW PPNONE(A5) ; doing all accounts? BNE RETDEV ; no - skip device summary OFILE SCNDDB(A5),OT$TRM + OT$TSP + OT$OFD TYPE MOV DEVPPN(A5),D1 ; display PPNs in device DCVT 0,OT$TRM + OT$LSP + OT$TSP TYPE MOV DEVFIL(A5),D1 ; display files in device DCVT 0,OT$TRM + OT$LSP + OT$TSP TYPE MOV DEVBLX(A5),D1 ; display blocks in device DCVT 0,OT$TRM + OT$LSP + OT$TSP TYPE MOV DEVSSF(A5),D0 ; sum of square of files MOV DEVPPN(A5),D1 ; number of PPNs CALL DIVIDE ; D2 = D0/D1 MOV D2,D0 ; D0 = D2 CALL ROOT ; D1 = SQR(D0) = R.M.S. DCVT 0,OT$TRM + OT$TSP TTYI ASCII |RMS files/PPN)| BYTE 15,0 EVEN TST DEVTOT(A5) ; totals only? JNE ENDDEV ; yes - bypass extra return ; apparently, repeated INIT (without DELMEM) does not allocate more memory ; DELMEM SCNDDB+D.BUF(A5) ; deallocate the buffer ; ANDB #^C,SCNDDB+D.FLG(A5) ; clear the INITed flag [110] ; ===== DONE WITH THIS DEVICE ===== RETDEV: CRLF ; blank line at end of this device ENDDEV: TST DEVONE(A5) ; doing only one device? BNE LSTDEV ; yes - we are done MOV DV.NXT(A4),A4 ; address of next DEVTBL entry CMP A4,#0 ; zero? (MOV An doesn't set Z) JNE NXTDEV ; no - go on to next device ; display grand totals LSTDEV: TST DEVONE(A5) ; doing all devices? JNE PRGEND ; no - done TYPE MOV SYSDEV(A5),D1 ; display devices in system DCVT 0,OT$TRM + OT$LSP + OT$TSP TYPE MOV SYSPPN(A5),D1 ; display PPNs in system DCVT 0,OT$TRM + OT$LSP + OT$TSP TYPE MOV SYSFIL(A5),D1 ; display files in system DCVT 0,OT$TRM + OT$LSP + OT$TSP TYPE MOV SYSBLX(A5),D1 ; display blocks in system DCVT 0,OT$TRM + OT$LSP + OT$TSP TYPE MOV SYSSSF(A5),D0 ; sum of square of files MOV SYSPPN(A5),D1 ; number of PPNs CALL DIVIDE ; D2 = D0/D1 MOV D2,D0 ; D0 = D2 CALL ROOT ; D1 = SQR(D0) = R.M.S. DCVT 0,OT$TRM + OT$TSP TTYI ASCII |RMS files/PPN)| BYTE 15,0 EVEN ; Overhead on AM-3000 = ( SYSSSF x .000008 ) seconds ; ~ ( SYSSSF x 573 / 2^32 ) minutes TYPE MOV SYSSSF(A5),D1 ; sum of squares of files CLRW D1 ; clear lower word SWAP D1 ; cheap divide by 65536 MUL D1,#573. ; * 573 CLRW D1 ; clear lower word SWAP D1 ; cheap divide by 65536 DCVT 0,OT$TRM + OT$TSP TYPECR PRGEND: EXIT ;---------------------; ; SQUARE ROOT ROUTINE ; ;---------------------; ; INPUT: D0 = any unsigned integer ; OUTPUT: D1 = square root of D0 (rounded) ; method: do binary search, starting with end values 0 and 2^16 ; USES: D2 (power) D3 (test) D4 (square) ROOT: MOV #32768.,D2 ; power = 2^15 CLR D1 ; root = 0 ROOT1: MOV D1,D3 ; test = root ADD D2,D3 ; + power MOV D3,D4 ; save test MUL D4,D3 ; square - test * test CMP D4,D0 ; is square too high? BHI 10$ ; yes - skip this power MOV D3,D1 ; no - test is a better root 10$: ASR D2,#1 ; go to next lower power BNE ROOT1 ; if not zero, try another test ; RTN ; returns D1 = INT(SQR(D0)) ; round root to nearest integer using: (root+1/2) ^ 2 = square+root + 1/4 MOV D1,D2 ; root MUL D2,D2 ; square ADD D1,D2 ; square + root CMP D2,D0 ; too high? BHI 20$ ; yes - don't round up INC D1 ; round up 20$: RTN ; returns D1 = INT(SQR(D0)+0.5) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ; Extended Divide Routine ; ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The 68000 can only divide 32 bits / 16 bits ===> 16-bit quot , 16-bit rem ; The routine below divides 32 bits / 32 bits ===> 32-bit quot , 32-bit rem ; This can be done in one instruction on the 68020 ; Reg Input Output ; --- ----------- --------- ; D0 Dividend Remainder ; D1 Divisor Divisor ; D2 [garbage] Quotient ; D3 [garbage] [zero] DIVIDE: ; Following is the 68000 routine CLR D2 ; D2 = quotient (clear first) MOV #1,D3 ; D3 = current quotient bit SHIFT1: CMP D0,D1 ; can we subtract? BLO TEST ; no - divisor is shifted enough ASL D1 ; shift again BCS BACK ; we went too far! ASL D3 ; quotient bit similiarly shifted BR SHIFT1 ; keep going BACK: ROXR D1 ; undo last shift TEST: CMP D0,D1 ; can we subtract? BLO SHIFT2 ; no - go shift SUB D1,D0 ; subtract divisor multiple ADD D3,D2 ; add bit to quotient SHIFT2: LSR D3 ; quotient bit halved BEQ ENDDIV ; if zero, we are done LSR D1 ; divisor multiple is halved BR TEST ; test next quotient subtract ENDDIV: RTN ; Following is the equivalent 68020 routine ; DIVUL D2:D0,D1 ; EXG D0,D2 ; RTN ; ===== IMPURE WORK AREA ===== ASECT .=0 SCNDDB: BLKB D.DDB ; directory DEVONE: BLKL 1 ; 0 (all devices) 1 (one device) PPNONE: BLKW 1 ; 0 (all accounts) ppn (one account) DEVTOT: BLKL 1 ; 1 (device totals only) 0 (do accounts) EXTMAX: BLKL 1 ; maximum # of extensions in table EXTCUR: BLKL 1 ; current # of extensions in table PPNCUR: BLKW 1 ; current PPN PPNBLX: BLKL 1 ; blocks in PPN DEVBLX: BLKL 1 ; blocks in device SYSBLX: BLKL 1 ; blocks in system PPNFIL: BLKL 1 ; files in PPN DEVFIL: BLKL 1 ; files in device SYSFIL: BLKL 1 ; files in system DEVSSF: BLKL 1 ; sum of squares of files in device SYSSSF: BLKL 1 ; sum of squares of files in system DEVPPN: BLKL 1 ; accounts in device SYSPPN: BLKL 1 ; accounts in system SYSDEV: BLKL 1 ; devices in system EXTASC: BLKB 4 ; work area for RAD50 to ASCII conversion MEMLTH: END .