****************************************************************
*                                                              *
         nam  Free
         ttl  OS9 Disk-stats Utility
*                                                              *
* This is a Y2K-compliant revision of the 'free' utility       *
* supplied with OS9 Level 2.  The 'created on' year displays   *
* as 4 digits, in the range 1900-2155.  Written by Richard S.  *
* Bair on March 19, 1999.                                      *
*                                                              *
****************************************************************
*
* include definitions in pass 1
         ifp1
         use  /dd/defs/OS9defs
         endc
*
         mod  CSIZE,NAME,TYPE,ATRV,START,DSIZE
NAME     fcs  /Free/
EDITION  fcb  $06
TYPE     set  Prgrm+Objct
REVS     set  $02
ATRV     set  ReEnt+REVS
*
* variable storage area
*
LDNG0FLG rmb  1 leading-zero flag when clear
FINGER   rmb  2 cur. loc. in output string
PATHNUM  rmb  1 path number to disk (@ mode)
SECTFREE rmb  3 count of free sectors
BGSTBLOK rmb  3 biggest free block so far
LASTBLOK rmb  3 free sectors in current block
OUTSTRNG rmb  $50 string storage (80 bytes)
SECTOR0  rmb  $3F info from disk i.d. sector
DDTOT    equ  SECTOR0
DDMAP    equ  SECTOR0+$04
DDBIT    equ  SECTOR0+$06
DDDAT    equ  SECTOR0+$1A
DDNAM    equ  SECTOR0+$1F
MAPEND   rmb  2 lcn of end of alloc. map area
ALLOCMAP rmb  $1000 space for (part of) D.A.M.
         rmb  250 space for stack
         rmb  200 space for parameters
DSIZE    equ  .
*
* storage space for constants
*
HELPTEXT fcb  $0A
         fcc  "Use: free [/diskname]"
         fcb  $0A
         fcc  /  tells how many disk /
         fcc  /sectors are unused/
         fcb  $0D
LNHLPTXT equ  *-HELPTEXT
CREATDON fcs  /" created on:/
CAPACITY fcs  /Capacity:/
SECTORS  fcs  / sectors (/
CLUSTERS fcs  /-sector clusters)/
FREESECT fcs  / free sectors, largest block/
SECTRS2 fcs  / sectors/
*
* code area
*
START    equ  *
*
         leay OUTSTRNG,u Get loc. of string area,
         sty  FINGER point to beginning of it.
         cmpd #$0000 Any parameters?
         beq  DFLTPRMS If not, assume data disk.
         lda  ,x+ Else get a byte of params;
         cmpa #$0D A C.R.?
         beq DFLTPRMS Then use default.
         cmpa #$2F A '/'?
         beq  GOPARSE Then go parse rest.
BADPRAMS leax HELPTEXT,pcr Else point to help text,
         ldy  #LNHLPTXT note its length,
         lda  #$02 path is stderrout,
         os9  I$WritLn have OS9 write it out,
         lbra OKEXIT then exit.
*
GOPARSE  leax -$01,x Move pointer back to start,
         pshs x save it temporarily.
         os9  F$PrsNam Have OS9 check pathstring.
         puls x Restore x,
         bcs  BADPRAMS exit if error,
LOOP1    lda  ,x+ else get a byte of params,
         lbsr ADDBYTE transfer to outstring area.
         subb #$01 Count down length (from OS9),
         bcc  LOOP1 loop until done.
DFLTPRMS lda  #$40 This is '@',
         lbsr ADDBYTE add it to outstring,
         lbsr ADDSPACE followed by a space.
         leax OUTSTRNG,u Move pointer to beginning
         stx  FINGER of string, store it.
         lda  #$01 Access mode='read'
         os9  I$Open Open path from disk @,
         sta  PATHNUM store path number.
         bcs  TOERROR If err, jump twice to exit.
*
         leax <SECTOR0,u Point to storage area,
         ldy  #$003F read 63 bytes
         os9  I$Read from LSN0 via OS9.
TOERROR  lbcs EXIT If err, carry code to exit.
         lbsr WRITLNOT Else write out a blank line,
         lda  #$22 place '"' at the beginning
         lbsr ADDBYTE of the next line.
         leay DDNAM,u Point to the disk name,
         lbsr XFRSTRNG copy it (+ space) to outstrng
         dec  FINGER+1 Delete the space,
         leay CREATDON,pcr point to '", created on:',
         lbsr XFRSTRNG add it to outstring.
         lbsr ADDDATE Then add the date and
         lbsr WRITLNOT write it out.
         leay CAPACITY,pcr Point to 'Capacity:',
         lbsr XFRSTRNG copy to outstring.
         leax DDTOT,u Point to disk size (3 bytes).
         lbsr TDEC3BYT Convert and add to outstrng.
         leay SECTORS,pcr Point to 'sectors',
         lbsr XFRSTRNG add it to outstring.
         dec  FINGER+1 Delete added space.
         ldd  DDBIT Get sectors-per-cluster,
         pshs b,a save these 2 bytes on stack,
         clr  ,-s add a third byte of zero.
         leax ,s Point to these three,
         lbsr TDEC3BYT convert and append them.
         leas $03,s Clean off the stack.
         leay CLUSTERS,pcr Point to 'clusters',
         lbsr XFRSTRNG add it to string.
         lbsr WRITLNOT Write string out.
*
         clra
         clrb Clear
         sta  SECTFREE variable
         std  SECTFREE+1 storage
         sta  LASTBLOK area
         std  LASTBLOK+1
         sta  BGSTBLOK
         std  BGSTBLOK+1
         lda  PATHNUM Get path # for disk @
         ldx  #$0000 MSW of seek loc. = 0.
         pshs u Need this register too.
         ldu  #$0100 LSW = 256 (beg. of LSN 1)
         os9  I$Seek Put file pointer there.
         puls u Restore register,
LOOP2    leax ALLOCMAP,u point to space for usage map.
         ldd  #$1000 Space is only 4k.
         cmpd DDMAP Is map bigger than that?
         bls  SKIP1 If yes, just get 4k.
         ldd  DDMAP Else get all of it.
SKIP1    leay d,x Point to end of map area,
         sty  MAPEND record pointer.
         tfr  d,y No. of bytes to read into y,
         lda  PATHNUM get disk @ path number,
         os9  I$Read read it.
         bcs  EXIT Exit on error.
*
LOOP3    lda  ,x+ Get a byte from alloc. map,
         bsr  EVALBYTE evaluate it.
         stb  ,-s Store bit count on stack.
         beq  SKIP2 If it's zero, skip.
LOOP4    ldd  SECTFREE+1 Else get count (middle, LSB),
         addd DDBIT add cluster size
         std  SECTFREE+1 and store result.
         bcc  SKIP3 Was there a carry?
         inc  SECTFREE If so, increase MSB.
SKIP3    dec  ,s Reduce bit count,
         bne  LOOP4 loop if not zero yet.
SKIP2    leas $01,s Erase that byte.
         cmpx MAPEND Are there more to do?
         bcs  LOOP3 If so, loop back.
         ldd  DDMAP Get size of alloc. map,
         subd #$1000 reduce by what's been read,
         std  DDMAP record remainder.
         bhi  LOOP2 If  more to read, loop.
*
         bsr  NOTFREE Else figure out totals,
         leax SECTFREE,u point to number free,
         lbsr TDEC3BYT convert and add to outstring.
         leay FREESECT,pcr Point to 'Free sect', etc.,
         bsr  XFRSTRNG put it in outstring.
         leax BGSTBLOK,u Point to biggest block count,
         lbsr TDEC3BYT convert and add.
         leay SECTRS2,pcr Point to 'sectors',
         bsr  XFRSTRNG add it to outstring.
         bsr  WRITLNOT Write the string out,
         lda  PATHNUM get the path number
         os9  I$Close and close it.
         bcs  EXIT If error, carry code out.
OKEXIT   clrb Else indicate no error,
EXIT     os9  F$Exit return to caller.
*
EVALBYTE clrb Count = 0 to start.
         cmpa #$FF None of these clusters free?
         beq  NOTFREE Then skip to save time.
         bsr  X4 Do the bit check
X4       bsr  X2 eight
X2       bsr  X1 times in all.
X1       asla Look at the leftmost bit.
         bcs NOTFREE If in-use/bad, jump.
         incb Else up free-cluster count,
         pshs b,a save registers,
         ldd  LASTBLOK+1 get current-block free count,
         addd DDBIT add 1 cluster,
         std  LASTBLOK+1 and save.
         bcc  SKIP4 If no carry, skip.
         inc  LASTBLOK Else increase MSB.
SKIP4    puls a,b,pc Restore regs and return.
*
NOTFREE  pshs b,a Save registers,
         ldd LASTBLOK get current free size (MSB+).
         cmpd BGSTBLOK Bigger than biggest-so-far?
         bhi  SKIP5 Then go update biggest.
         bne  SKIP6 Smaller?  Reset and return.
         ldb  LASTBLOK+2 If same, then
         cmpb BGSTBLOK+2 compare LSB's,
         bls  SKIP6 branch accordingly.
SKIP5    sta  BGSTBLOK Save new MSB,
         ldd  LASTBLOK+1 get middle and LSB
         std  BGSTBLOK+1 and record them.
SKIP6    clr  LASTBLOK Clear the
         clr  LASTBLOK+1 last-block-size
         clr  LASTBLOK+2 bytes,
         puls a,b,pc restore registers and return.
*
XFRSTRNG lda  ,y Look at a byte.
         anda #$7F Clear sign bit
         bsr ADDBYTE and write it.
         lda  ,y+ Look at it again;
         bpl  XFRSTRNG loop if not a terminator.
ADDSPACE lda  #$20 A space -
ADDBYTE  pshs x Store x temporarily,
         ldx  FINGER get string pointer,
         sta  ,x+ put this byte there.
         stx  FINGER Record updated pointer.
         puls x,pc Restore x and return.
*
WRITLNOT pshs y,x,a Save registers,
         lda  #$0D append a C.R.
         bsr ADDBYTE to string,
         leax OUTSTRNG,u point to beginning of it,
         stx  FINGER save that pointer,
         ldy  #$50 write maximum 80 characters
         lda  #$01 to stdout
         os9  I$WritLn via OS9.
         puls a,x,y,pc Restore registers and return.
*
TENMILYN fcb $98,$96,$80 equals 10,000,000
         fcb $0F,$42,$40 equals 1,000,000
         fcb $01,$86,$A0    =  100,000
         fcb $00,$27,$10    =  10,000
         fcb $00,$03,$E8    =  1,000
         fcb $00,$00,$64    =  100
         fcb $00,$00,$0A    =  10
         fcb $00,$00,$01    =  1
*
TDEC3BYT lda #$0A Count 8 digits and 2 commas.
         pshs y,x,b,a Save registers,
         leay <TENMILYN,pcr point to 10,000,000 binry.
         clr  LDNG0FLG Set leading-zero flag.
         ldb  ,x Get MSB in register b,
         ldx  $01,x middle and LSB in x.
LOOP5    lda  #$FF Start with digit = -1.
LOOP6    inca Increase digit,
         exg  d,x Subtract current
         subd $01,y place value from
         exg  d,x middle and LSB,
         sbcb ,y and from MSB.
         bcc  LOOP6 Repeat 'til we pass zero.
         bsr  LDNG0TST Go write it (if not ldng 0).
         exg  d,x Put balance
         addd $01,y back to
         exg  d,x non-negative
         adcb ,y (MSB too).
         leay $03,y Move to next digit value.
         dec  ,s Count down # of digits done.
         beq  TDECEXIT If no more, exit.
         lda  ,s Get digit count.
         cmpa #$01 Last one?
         bne  SKIP7 If not, skip.
         sta  LDNG0FLG Set flag to print for sure.
SKIP7    bita #$03 Check:  time for a comma?
         bne  LOOP5 If not, loop back.
         dec  ,s Else reduce digit count.
         tst  LDNG0FLG Are we writing yet?
         beq  LOOP5 If not, forget comma, loop.
         lda  #$2C This is a comma.
         bsr  ADDBYTE Add it to outstring,
         bra  LOOP5 then loop back.
TDECEXIT puls a,b,x,y,pc Done;  restore and return.
*
ADDDATE  leax DDDAT,u Point to date/time packet.
         bsr  YEAR Get year and decode.
         bsr  SLSHPLUS Add '/', do month.
SLSHPLUS lda  #$2F Get '/',
         lbsr ADDBYTE add it to outstring.
BINTODEC clr  LDNG0FLG No non-zero digits yet.
         ldb  ,x+ Get binary byte.
         lda  #$FF Start with -1,
LOOP7    inca increase digit by 1,
         subb #$64 decrease binary by 100.
         bcc  LOOP7 Loop 'til we pass 0.
         bsr  LDNG0TST (maybe) Write a digit.
TWODIGTS lda  #$0A For 10's digt start with 9+1.
         sta  LDNG0FLG Flag to print regardless.
LOOP8    deca count digit down,
         addb #$0A binary up by 10,
         bcc  LOOP8 Loop 'til we cross 0 again.
         bsr  LDNG0TST Write the digit.
         tfr  b,a Put remaindr into b register.
LDNG0TST tsta Is it a zero?
         beq  SKIP8 If so, skip.
         sta  LDNG0FLG Else set not-ldng-0 flag.
SKIP8    tst  LDNG0FLG Leading 0?
         bne  TOASCII If not, write it out.
         rts Else return.
*
YEAR     ldb  ,x+ Get year byte (binary),
         lda  #18-100 set up for century digts.
LOOP9    inca Increase century,
         subb #$64 reduce binary by 100,
         bcc  LOOP9 until we pass zero.
         pshs b Save the binary remainder,
         tfr  a,b put century count to b,
         bsr  TWODIGTS write 2 century digits.
         puls b Retrieve remainder (bin),
         bra  TWODIGTS write other 2 & return.
*
TOASCII  adda #$30 Change to ASCII,
         lbra ADDBYTE write, return from there.
*
         emod Append CRC bytes.
CSIZE    equ  *
