******************************************************************
*                                                                *
         nam  Dir
         ttl  OS9 Disk-directory Utility
*                                                                *
* This is a Y2K-compliant revision of the 'dir' utility supplied *
* with OS9 Level 2.  'dir e' commands on narrow windows show the *
* correct 2-digit year under 'Modified on'.  Displays on screens *
* 64 characters wide or more (and redirected output) show years  *
* as 4 digits.  Written by Richard S. Bair on March 12, 1999.    *
*                                                                *
******************************************************************
*
* include definitions in pass 1
         ifp1
         use  /dd/defs/OS9defs
         endc
*
         mod  CSIZE,NAME,TYPE,ATRV,START,DSIZE
NAME     fcs  /Dir/
EDITION  fcb  $09
TYPE     set  Prgrm+Objct
REVS     set  $02
ATRV     set  ReEnt+REVS
*
* variable storage area
*
PATHSTR  rmb  2 pointer to path string
PATHNUM1 rmb  1 path number to dir file
EFLAG    rmb  1 dir e flag
XFLAG    rmb  1 dir x flag/mask
PATHNUM2 rmb  1 path # to file (for e info)
LDNG0FLG rmb  1 Leading-zero flag when clear.
         rmb  1
COLWIDTH rmb  1 column width
LASTCOL  rmb  1 loc. of last column
WDTHFLAG rmb  1 flag for display width
FINGER   rmb  2 cur. loc. in output string
DATETIME rmb  6 space for date/time packet
DIRECORD rmb  32 space for directory record
FILEDESC rmb  13 space for file descriptor
OUTSTRNG rmb  80 space for output string
         rmb  250 space for stack
         rmb  200 space for parameters
DSIZE    equ  .
*
* storage space for constants
*
DIROF    fcb  $0A
         fcs  / Directory of /
DOT      fcb  '.
         fcb  $0D
AT       fcb  '@
         fcb  $0D
WIDEHEDR fcb  $0D
         fcb  $0A
         fcc  /Owner  Last modified   Attributes /
         fcc  /Sector Bytecount   Name/
         fcb  $0D
         fcb  $0A
         fcc  /----- ---------------- ---------- /
         fcc  /------ --------- ----------/
         fcb  $0D
         fcb  $0A
LENWDHDR equ  *-WIDEHEDR
NARWHEDR fcb  $0D
         fcb  $0A
         fcc  /Modified on  Owner   Name/
         fcb  $0D
         fcb  $0A
         fcc  /  Attr     Sector     Size/
         fcb  $0D
         fcb  $0A
         fcc  /===============================/
         fcb  $0D
         fcb  $0A
LENNRHDR equ  *-NARWHEDR
*
* code area
*
START    equ  *
*
         leay OUTSTRNG,u Point to outstring space,
         sty  FINGER store pointer.
         clr  XFLAG Clear 'dir x' flag
         clr  EFLAG and 'dir e' flag.
         clr  WDTHFLAG Set default to wide display,
         lda  #16 with columns 16 wide,
         ldb  #48 last one starts at 48.
         std  COLWIDTH Record these params.
         pshs y,x,b,a Store registers.
         lda  #$01 Path is stdout
         ldb  #$26 Code for SS.ScSiz
         os9  I$GetStt Get window size info frm OS9.
         bcc  GOTSIZE If no error, jump.
         cmpb #$D0 "illegal service request"?
         beq  DEFLTSIZ Then proceed w. default size.
         puls a,b,x,y Else bail out;  restore regs,
         lbra EXIT write a C.R. and exit.
*
GOTSIZE  cmpx #64 Screen 64 cols or more?
         bge  DEFLTSIZ Then proceed w large display.
         inc  WDTHFLAG Else set flag to "narrow",
         lda  #10 column width to 10,
         ldb  #20 last column at 20,
         std  COLWIDTH record these values.
DEFLTSIZ puls a,b,x,y Now retrieve registers,
         lbsr PARSER parse parm strng for options.
         lda  ,-x Was last byte a C.R.?
         cmpa #$0D If not, leave reg x pointing
         bne  NOTDOT to rest of param string.
         leax DOT,pcr Else move pointer to '.'
NOTDOT   stx  PATHSTR Save pointer.
         lda  #$81 Access mode = "d------r"
         ora  XFLAG If exec. dir, then "d----e-r"
         pshs x,a Save registers,
         os9  I$Open open a path to the directory.
         sta  PATHNUM1 Store the path number,
         puls a,x Restore x = pathname pointer.
         lbcs EXIT If error, write CR and exit.
         os9  I$ChgDir Chd (or chx) to pathname.
         lbcs EXIT If error, write CR and exit.
         pshs x (Points past param. string.)
         leay DIROF,pcr Point to display title.
         lbsr TRNSFSTR Put it in write-out string.
         ldx  PATHSTR Now point to path string.
LOOP1    lda  ,x+ Get a byte,
         lbsr ADDBYTE add it to outstring,
         cmpx ,s compare loc. with end marker.
         blo  LOOP1 Repeat until done.
         leas $02,s Now forget that pointer.
         lbsr PARSER ??
         lbsr ADDSPACE Add a space to outstring,
         lbsr ADDSPACE and another.
         leax DATETIME,u Space for date/time packet.
         os9  F$Time Fetch information.
         leax DAtetime+3,u Point to just time.
         lbsr WRITIME Ch to ASCII, add to outstrng.
         lbsr WRITLOUT Write string out.
*
         tst  EFLAG Extended directory?
         beq  BODY If not, don't write a header.
         lda  #$01 Access mode = "-------r",
         ora  XFLAG or "-----e-r" if 'dir x'.
         leax AT,pcr Point to '@',
         os9  I$Open open another path this way.
         lbcs EXIT Exit on error.
         sta  PATHNUM2 Save this path number.
         tst  WDTHFLAG Narrow display?
         bne  GOWTHNAR If so, skip.
         leax WIDEHEDR,pcr Point to header string,
         ldy  #LENWDHDR note its length,
         bra  SKIP1 skip over code for narrow.
GOWTHNAR leax NARWHEDR,pcr Point to narrow header,
         ldy  #LENNRHDR note its length.
SKIP1    lda  #$01 Path is stdout;
         os9  I$Write write header string.
*
BODY     lda  PATHNUM1 Get directory path number.
         ldx  #$0000 Set MSW = 0;
         pshs u Need this register too,
         ldu  #0064 for LSW. (Past '.' and '..'.)
         os9  I$Seek Set file pointer there.
         puls u Restore u.
         lbra READRCRD Go read a record.
*
BODYLOOP tst  DIRECORD Is first byte CHR$(0)?
         lbeq READRCRD If so, discard, get next one.
         tst  EFLAG Is good.  Need extended info?
         bne  EXTINFO If so, detour.
         leay DIRECORD,u Else point to filename strng,
         lbsr TRNSFSTR transfer it to write-out str.
ADDSPCE  lbsr ADDSPACE Append a space.
         ldb  FINGER+1 Endlocation of write-out str.
         subb #OUTSTRNG minus beginning = length.
         cmpb LASTCOL Past last column?
         bhi  WRITREAD Write out & get new filename.
LOOP2    subb COLWIDTH Else pad string with spaces
         bhi  LOOP2 to get to next available
         bne  ADDSPCE column.
         bra  READRCRD Now go get another filename.
*
EXTINFO  pshs u We'll need to use this reg.
         lda  DIRECORD+31 Get LSB of file's sector #,
         clrb mult. by 256 = byte #.
         tfr  d,u Put resulting word into u.
         ldx  DIRECORD+29 Get MSW into x.
         lda  PATHNUM2 Set '@' path number,
         os9  I$Seek find File Descriptor sector.
         puls u Restore register.
         bcs  EXIT Exit on error.
         leax FILEDESC,u Point to data space,
         ldy  #$000D get first 13 bytes of
         os9  I$Read file descriptor.
         bcs  EXIT Exit on error.
         tst  WDTHFLAG Narrow display?
         bne  EBODYNAR If so, jump.
         ldd  FILEDESC+1 Get 'owner' doublebyte.
         clr  LDNG0FLG Set leading-zero flag.
         bsr  TOHEX4DG Conv. to hex, add to outstr.
         lbsr ADDSPACE Append a space.
         lbsr WDATETIM Write date last modified.
         lbsr ADDSPACE
         lbsr ATTRIBS Write attributes to outstrng.
         lbsr ADDSPACE
         lbsr ADDSPACE
         bsr  STSECTOR Write start sector (in hex).
         bsr  FILESIZE Now the file size (hex).
         leay DIRECORD,u Point to filename,
         lbsr TRNSFSTR add it to outstring.
WRITREAD lbsr WRITLOUT Write out current line,
         bra  READRCRD Go get next filename.
*
EBODYNAR lbsr WDATETIM Write date last modified,
         ldd  FILEDESC+1 point to "owner",
         clr  LDNG0FLG set "leading 0" flag,
         bsr  TOHEX4DG write it to outstring.
         bsr  ADDSPACE
         leay DIRECORD,u Point to filename,
         lbsr TRNSFSTR add it to outstring.
         lbsr WRITLOUT Write out current line.
         lbsr ATTRIBS Write attribute string,
         bsr  ADDSPACE
         bsr  ADDSPACE
         bsr  STSECTOR followed by starting sector,
         bsr  FILESIZE then the file size.
         lbsr WRITLOUT Write out this line.
*
READRCRD leax DIRECORD,u Dir entry storage area.
         ldy  #$0020 Records are 32 bytes long.
         lda  PATHNUM1 Get directory path #.
         os9  I$Read Read the next record.
         lbcc BODYLOOP If no error, loop back.
         cmpb #$D3 Is error EOF?
         bne  EXIT If not, leave code in b.
         clrb Else indicate no error.
EXIT     lbsr WRITLOUT Write out last line
         os9  F$Exit and exit.
*
STSECTOR lda  DIRECORD+29 Get MSB of starting sector,
         bsr  BINTOHEX convert to hex and write.
         ldd  DIRECORD+30 Now get LSW of it,
TOHEX4DG bsr  BINTHEX2 conv. high byte and write.
         tfr  b,a Now look at low byte.
         bsr  HIGHNIBL Conv. high nibble, write.
         inc  LDNG0FLG Set flag to write
         bsr  LOWNIBL last nibble even if 0.
         bra  ADDSPACE Add a space and return.
*
FILESIZE ldd  FILEDESC+$09 Get MSW of file size,
         bsr  BINTOHEX conv. and write to outstring.
         tfr  b,a Now look at LSB (of MSW).
         bsr  BINTHEX2 Convert it and write.
         bsr  ADDSPACE Insert a space.
         ldd  FILEDESC+$0B Now get LSW of file size,
         bra  TOHEX4DG convert, write, return.
*
HIGHNIBL pshs a Save the binary byte,
         lsra look at
         lsra only the
         lsra high nibble
         lsra of it.
         bsr  HEXDIGIT Conv., write to outstring.
         puls a,pc Restore binary to a, return.
*
BINTOHEX clr  LDNG0FLG Set leading-zero flag.
BINTHEX2 bsr  HIGHNIBL Conv. high nibble, write.
LOWNIBL  anda #$0F Mask out high nibble.
HEXDIGIT tsta Rest = 0?
         beq  SKIP2 Then maybe replace w. space.
         sta  LDNG0FLG Else set "not leading zero".
SKIP2    tst  LDNG0FLG A leading 0?
         bne  SKIP3 If not, then use it.
         lda  #$20 Else replace with a space,
         bra  ADDBYTE write it and return.
SKIP3    adda #$30 Convert to ASCII digit.
         cmpa #$39 Is it 9 or less?
         bls  ADDBYTE Then write and return.
         adda #$07 Else move to "A - F",
         bra  ADDBYTE write and return.
*
ADDSPACE lda  #$20 A space --
ADDBYTE  pshs x Save x temporarily,
         ldx  FINGER find end of outstring.
         cmpx #OUTSTRNG+80,u Time to write some out?
         blo  SKIP4 If not, proceed.
         bsr  WRITLOT2 Else write first.
         ldx  FINGER Reset pointer.
SKIP4    sta  ,x+ Add character to it.
         stx  FINGER Save updated pointer.
         puls x,pc Restore x and return.
*
ATTSTRNG fcc  /dsewrewr/
         fcb  $FF
ATTRIBS  ldb  FILEDESC+0 Get attributes byte.
         leax <ATTSTRNG,pcr Point to attribute string.
         lda  ,x+ Get one;
LOOP3    aslb Is this attribute set?
         bcs  SKIP5 If so, skip.
         lda  #$2D Else replace with '-',
SKIP5    bsr  ADDBYTE add to outstring.
         lda  ,x+ Get next attribute.
         bpl  LOOP3 End?  If not, loop back.
         rts Else return.
*
TRNSFSTR lda ,y Get a byte from instring,
         anda #$7F Cancel high bit if set.
         bsr  ADDBYTE Add it to outstring.
         lda  ,y+ Look at it again;
         bpl  TRNSFSTR if high bit is clear, loop.
         rts Else return.
*
WRITLOT2 pshs y,x,b,a Save registers,
         bra  WRITLOT3 skip C.R.
WRITLOUT pshs y,x,b,a Save registers.
         lda  #$0D Append a CR
         bsr  ADDBYTE to outstring,
WRITLOT3 leax OUTSTRNG,u point to beginning of it,
         stx  FINGER reset pointer to beginning,
         ldy  #$0050 write up to 80 characters
         lda  #$01 to stdout
         os9  I$WritLn via OS9.
         puls a,b,x,y,pc Restore registers and return.
*
WDATETIM leax FILEDESC+3,u Point to date/time packet.
WDATTIM2 bsr  YEAR Convert year and write.
         bsr  SLSHNEXT Insert '/', then do month.
         bsr  SLSHNEXT Insert '/', then do day.
         bsr  ADDSPACE
         bsr  BINTODEC Convert hour and write.
         tst  WDTHFLAG Room for a colon?
         beq  SKIP6 Yes?  Skip narrow code.
         bsr  BINTODEC Narrow? Just write mins,
         bra  ADDSPACE space, and return.
SKIP6    bsr  COLNNEXT Colon, minutes.
         bra  ADDSPACE Add space and return.
*
SLSHNEXT lda  #$2F Load a '/',
         bra  SEPRNEXT then use code from WRITIME.
*
WRITIME  tst  WDTHFLAG Wide display or narrow?
         bne  NARWTIME Narrow, write only time.
         leax DATETIME,u Else point to date,
         bra  WDATTIM2 write date & time.
NARWTIME bsr  BINTODEC Convert hour and write.
COLNNEXT lda  #$3A Take ':',
SEPRNEXT bsr  ADDBYTE add to outstring.
         bra  BINTODEC Skip year-specific code.
YEAR     lda #18-100 Start with 1800,
         ldb  ,x get year byte.
LOOP4    inca Add a century,
         subb #100 reduce byte by same,
         bcc  LOOP4 loop if it's not yet negative.
         stb  ,x Save result temporarily.
         tfr  a,b Put century in b register.
         tst  WDTHFLAG Do we write it?
         bne  NARWYEAR Not if narrow display.
         bsr  TENSDIGT Go write two digits.
NARWYEAR ldb  ,x+ Retrieve year modulo 100.
         bra  TENSDIGT Write it always.
BINTODEC ldb  ,x+ Get binary value,
         lda  #$2F set ASCII base.
LOOP5    inca Count 100's digit up,
         subb #100 binary down,
         bcc  LOOP5 until we go negative.
         cmpa #$30 A leading zero?
         beq  TENSDIGT If so, write nothing.
         lbsr ADDBYTE Else add it to outstring.
TENSDIGT lda #$3A This time begin above '9',
LOOP6    deca count 10's digit down,
         addb #10 binary up,
         bcc LOOP6 until we cross zero again.
         lbsr ADDBYTE Add this digit to outstring.
         tfr  b,a Move rest to ASCII register,
         adda #$30 convert it,
         lbra ADDBYTE add to string and return.
*
PARSER   ldd  ,x+ Get 2 bytes from param strng.
         cmpa #$20 Is first one a space?
         beq  PARSER If so, loop (Advance 1 byte).
         cmpa #$2C Is it a comma?
         beq  PARSER Then loop.
         eora #$45 Is it an 'E'?
         anda #$DF or an 'e'?
         bne  XCHECK If not, go check for 'x'.
         cmpb #$30 Is next byte also alphanumrc?
         bcc  PARSEXIT If so, exit.
         inc  EFLAG If not, set 'extended' flag,
         bra  PARSER and loop to study next byte.
XCHECK   lda  -$01,x Reload last byte.
         eora #$58 Is it an 'X'?
         anda #$DF or an 'x'?
         bne  PARSEXIT If not, exit.
         cmpb #$30 Else is next byte alphanumrc?
         bcc  PARSEXIT If so, exit.
         lda  #$04 Else put 'x' attribute
         sta  XFLAG into 'execution dir' flag,
         bra  PARSER loop to check next byte.
PARSEXIT rts Return.
*
         emod Append CRC bytes.
CSIZE    equ  *
