.Title Frag_free Find free space fragmentation of a disk ; NOTE: This program requires BYPASS privilege to open the ; BITMAP.SYS file. ;---------------------------------------------------------------- ; ; Macros ; ;---------------------------------------------------------------- ; MACRO to do error checking .Macro Erjmp Destination,?Labl Blbs R0,Labl Jmp Destination Labl: .Endm ; MACRO to define item lists .Macro Item Type,Length,BufAddress,Retlen .Word Length .Word Type .Address BufAddress .If blank .Long 0 .endc .If not_blank .Address Retlen .Endc .Endm ;---------------------------------------------------------------- ; ; Storage ; ;---------------------------------------------------------------- .Psect Data ; ; storage for opening the BITMAP.SYS file ; FilFAB: $FAB FAC=,SHR=, - FOP=UFO,FNA=Inputbuffer Filename: .Ascii /[000000]BITMAP.SYS/ Filenamelen: .Long .-Filename Inputbufferdsc: .Long 80 .Address Inputbuffer Inputbuffer: .Blkb 80 Prompt: .Ascid /Disk device to check: / Channel: .Blkl 1 IOSB: .Blkq 1 ; ; storage for bitmap scan ; SaveLongword: .Blkl 1 SaveBit: .Blkl 1 ; ; storage for GETDVI ; DevChar: .Blkl 1 DevClass: .Blkl 1 MaxBlock: .Blkl 1 Cluster: .Blkl 1 ItemList: Item DVI$_DEVCHAR,4,Devchar Item DVI$_DEVCLASS,4,DevClass Item DVI$_MAXBLOCK,4,Maxblock Item DVI$_CLUSTER,4,Cluster .Long 0 ;terminator BadDeviceMsg: .Ascid /?Device must be a Files-11 mounted disk/ ; ; storage for range output ; Parameters: ; FAOL parameter list Bottom_limit: .Blkl 1 ; these variables MUST be in Top_limit: .Blkl 1 ; this order to match the Count: .Blkl 1 ; FAO control string Blocks: .Blkl 1 Percent: .Blkl 1 Decimal: .Blkl 1 CumPoints: .Blkl 1 CumDecimal: .Blkl 1 Total_parameters: Total_count: .Blkl 1 Total_Blocks: .Blkl 1 Header: .Ascid / Range Extents Blocks Percentage Cumulative/ Control: .Ascid /!6UL --> !6UL !5UL !7UL !5UL.!2UL !5UL.!2UL/ TotalHeader: .Ascid / ------- ------/ TotalControl: .Ascid / !5UL !7UL/ Outbufdsc: .Long 80 .Address Outbuf OutBuf: .Blkb 80 ; ; storage for ranges ; RANGE_TABLE contains the lower limit of each range ; RANGE_COUNT contains the count of extents within the parallel ; range ; BLOCK_COUNT contains the count of blocks within the parallel ; range ; TOTAL_COUNT is the total number of free extents (above) ; TOTAL_BLOCKS is the total number of free blocks on the disk (above) ; ; NOTE: RANGE_TABLE and RANGE_COUNT are parallel tables and ; should contain the same number of entries. Range_Table: .Long 0 ;start with zero .Long 10 .Long 20 .Long 40 .Long 80 .Long 100 .Long 500 .Long 1000 .Long 2000 .Long 5000 .Long 10000 .Long 20000 .Long ^X7FFFFFFF ;terminator for table Range_Count: .Blkl 12 ;count should be number of entries ; in RANGE_TABLE Block_Count: .Blkl 12 ;count should be number of entries ; in RANGE_TABLE ; ; storage for bitmap pages ; .Psect Pages,Page Myblock: .Blkb <512*300> ;enough for a big bitmap ;---------------------------------------------------------------- ; ; Start of code ; ;---------------------------------------------------------------- .Psect Code .Entry Frag_free,^M<> ; get the disk name Pushal InputbufferDsc ;where to put length Pushal Prompt ;address of prompt Pushal InputbufferDsc ;where to put the string CallS #3,G^LIB$GET_FOREIGN ;get the command line Erjmp Error ; check the characteristics of the given device $Getdviw_S Devnam=Inputbufferdsc, - ;name of device Itmlst=ItemList ;item list Erjmp Error ; check for disk, files-oriented, mounted, directory structured Cmpl #DC$_DISK,DevClass ;disk? Bneq BadDevice ;no - bad device Bitl #DEV$M_FOD,DevChar ;files oriented? Beql BadDevice ;no - bad device Bitl #DEV$M_DIR,DevChar ;directory structured? Beql BadDevice ;no - bad device Bitl #DEV$M_MNT,DevChar ;mounted? Beql BadDevice ;no - bad device Jmp GoodDevice ;good device, use it ; Bad device BadDevice: Pushal BadDeviceMsg CallS #1,G^LIB$PUT_OUTPUT ;output error Erjmp Error Jmp Error ; Good device...set up name of bitmap and open it GoodDevice: Movl InputbufferDsc,R6 ;get offset into register Movc3 Filenamelen,Filename,InputBuffer(R6) ;move in file name Addl2 Filenamelen,InputBufferDsc ;get total length Movb InputBufferDsc,FilFAB+FAB$B_FNS ;size of file name $Open FilFAB ;try to open the file Erjmp Error Movl FilFAB+FAB$L_STV,Channel ;grab the channel ; we now have a channel to the file, calculate number of bits in the ; bitmap (= MAXBLOCKS / CLUSTER) Divl3 Cluster,MaxBlock,R11 ;get number of bits Addl3 #4095,R11,R10 ;setup for round up Divl2 #4096,R10 ;get number of pages Mull3 #512,R10,R9 ;get number of bytes Addl2 #31,R11 ;round up longwords Divl3 #32,R11,R10 ;get number of longwords ; we have the number of pages in the bitmap, so bring it in $Qiow_S Chan=Channel, - ;channel to read Func=#IO$_READVBLK, - ;read some blocks IOSB=IOSB, - ;I/O status block P1=Myblock, - ;where to put it P2=R9, - ;number of bytes P3=#2 ;block 2 is where to start Erjmp Error Movzwl IOSB,R0 ;get "real" status Erjmp Error ;check it again ; we have the bitmap in memory, deassign BITMAP.SYS channel $Dassgn_S Chan=Channel Erjmp Error ;---------------------------------------------------------------- ; ; Start into the scan loop. We simply want to count the number ; of free extents in the bitmap that are in certain ranges. ; The ranges are defined in RANGE_TABLE, and the count ; of extents in each range are kept in RANGE_COUNT. These ; are parallel tables. The total count of extents is ; kept in TOTAL_COUNT. ; ; It should be noted that in the bitmap, a 1 bit indicates ; a FREE cluster. ; ; Register usage: ; R4 - pointer into RANGE_TABLE (in CATEGORIZE loop) ; R5 - count of bits in current extent (in CATEGORIZE loop) ; R6 - base of bitmap array ; R7 - current longword counter for bitmap scan ; R8 - current bit number in the current longword ; R9 - temporary variable ; R10 - total longword count in bitmap (limit value) ; ; ;---------------------------------------------------------------- Moval Myblock,R6 ;get address of array Clrl R7 ;longword count Clrl R8 ;bit offset Find_free_Loop: Subl3 R8,#32,R9 ;get space left in this longword Movl (R6)[R7],R5 ;get the longword FFS R8,R9,R5,R8 ;is there a set bit? Bneq FoundFree ;yes - start counting extent ; no free bit in this longword, clear startbit and go to next longword Clrl R8 ;clear bit position Incl R7 ;increment longword count Cmpl R7,R10 ;compare to number we need Blss Find_Free_loop ;and loop ; end of the bitmap, output statistics Jmp OutStats ;output statistics ; ; Here we found a free bit in the current longword. So, we record ; this position, then start scanning for a used bit. ; FoundFree: Incl Total_Count ;increment total count Movl R7,SaveLongword ;save longword count Movl R8,SaveBit ;save bit position Find_used_loop: Subl3 R8,#32,R9 ;get count of rest of longword Movl (R6)[R7],R5 ;get the longword FFC R8,R9,R5,R8 ;find a clear bit Bneq FoundUsed ;found a used bit, count the extent ; no used bit found, increment longword, continue Clrl R8 ;clear bit count Incl R7 ;increment longword count Cmpl R7,R10 ;check against limit Blss Find_Used_loop ;continue loop ; found end of free extent, calculate this extent, then output stats FoundUsed: Subl3 SaveLongword,R7,R5 ;get count of longwords Bneq ManyLongwords ;any full longwords? Subl3 Savebit,R8,R5 ;get count in this longword Brb GotCount ;we have total, so count extent ManyLongWords: Decl R5 ;don't count end longwords Mull2 #32,R5 ;get bits of full longwords Subl3 Savebit,#32,SaveBit ;get bits in first longword Addl2 SaveBit,R5 ;add in from first longword Addl2 R8,R5 ;add in from last longword ; ; Total count of bits is in R5 at this point. Calculate extent size ; by multiplying by CLUSTER, then categorize this extent. GotCount: Mull2 Cluster,R5 ;get extent size Addl2 R5,Total_Blocks ;add in to total blocks Clrl R4 ;pointer into table Categorize: Cmpl R5,Range_table[R4] ;check against this entry Blss Foundit ;if less, found it Incl R4 ;increment pointer Jmp Categorize ;and loop Foundit: Decl R4 ;actually went one too far Incl Range_count[R4] ;increment count Addl2 R5,Block_Count[R4] ;add in blocks Cmpl R7,R10 ;out of range? Bgeq Outstats ;output stats Jmp Find_free_loop ;and go to finding a free bit ;---------------------------------------------------------------- ; ; output statistics ; ; Register usage: ; R6 - pointer into RANGE_COUNT table ; R7 and R8 - percentage calculation registers ; ;---------------------------------------------------------------- OutStats: Pushal Header ;address of header CallS #1,G^LIB$PUT_OUTPUT ;output header Erjmp Error Clrl R6 ;pointer into range tables Clrl CumPoints ;cumulative points Clrl CumDecimal ;cumulative decimal points RangeLoop: Mull3 #10000,Block_count[R6],R7;get percentage points Divl3 Total_Blocks,R7,R8 ;get percentage * 100 Clrl R9 ;make quadword value Ediv #100,R8,Percent,Decimal ;R8 = percent, R7 = decimal Addl2 Percent,CumPoints ;get cumulative Addl2 Decimal,CumDecimal ;get cumulative decimal Cmpl CumDecimal,#100 ;too much? Blss OKdecimal Incl CumPoints ;increment points Subl2 #100,CumDecimal ;subtract out OKdecimal: Movl Range_table[R6],Bottom_limit ;get bottom limit Cmpl Bottom_limit,#^X7FFFFFFF ;end of list? Bneq OKbottom ;still in table Jmp AllDone ;all done OKbottom: Movl Range_table+4[R6],Top_limit ;get top limit Movl Range_count[R6],Count ;get count Movl Block_Count[R6],Blocks ;get blocks Movl #80,OutbufDsc ;reset descriptor $Faol_S Ctrstr=Control, - ;control string Outlen=OutbufDsc, - ;where to put length Outbuf=OutbufDsc, - ;where to put the output Prmlst=Parameters ;parameter list Erjmp Error Pushal OutBufDsc ;setup for output CallS #1,G^LIB$PUT_OUTPUT ;output the data Incl R6 ;increment pointer Jmp RangeLoop ;and loop AllDone: Pushal TotalHeader CallS #1,G^LIB$PUT_OUTPUT ;output lines for totals Erjmp Error Movl #80,OutBufDsc $Faol_S Ctrstr=TotalControl, - ;control string Outlen=OutbufDsc, - ;where to put length Outbuf=OutbufDsc, - ;where to put the output Prmlst=Total_Parameters ;parameter list Erjmp Error Pushal OutBufDsc CallS #1,G^LIB$PUT_OUTPUT Erjmp Error Error: $Exit_S R0 .End Frag_free