;***************** BDOS file system part 2 *****************
;
scndm:
        ;scan the disk map addressed by dptr for non-zero
        ;entries, the allocation vector entry corresponding
        ;to a non-zero entry is set to the value of CL (0,1)
        ;BX addresses the beginning of the directory entry
;
        CALL    getdptra                ;BX = buffa + dptr
        ADD     BX,dskmap               ;BX = diskmap address
        PUSH    CX                      ;save the 0/1 bit to set
        MOV     CL,fcblen-dskmap+1      ;size of single byte diskmap+1
scndm0:
                                        ;loop for each diskmap entry
        POP     DX                      ;recall bit parity
        DEC     CL
        JNZ     L_41
        RET
L_41:                                   ;all done scanning?
                                        ;no, get next entry for scan
        PUSH    DX                      ;replace bit parity
        cmp     single,0
        JZ      scndm1
                                        ;single byte scan operation
        PUSH    CX                      ;save counter
        PUSH    BX                      ;save map address
        MOV     CL,[bx]
        MOV     CH,0                    ;CX=block#
        jmps    scndm2
scndm1:
                                        ;double byte scan operation
        DEC     CL                      ;count for double byte
        PUSH    CX                      ;save counter
        MOV     cx,[bx]                 ;CX=block
        INC     BX
        PUSH    BX                      ;save map address
scndm2:
                                        ;arrive here with CX=block#,
                                        ;DL= 0/1
        OR      cx,cx                   ;skip if = 0000
        JZ      scan3
        MOV     BX,maxall               ;check invalid index
        cmp     bx,cx
        JNAE    L_42
        CALL    setallocbit
L_42:                                   ;
                                        ;bit set to 0/1
scan3:                                  ;
        POP     BX
        INC     BX                      ;to next bit position
        POP     CX                      ;recall counter
        jmps    scndm0                  ;for another item
;
initialize:
        ;initialize the current disk
        ;lret = false ;set to true if $ file exists
        ;compute the length of the allocation vector - 2
        MOV     BX,maxall
        MOV     CL,3                    ;perform maxall/8
        shr     bx,cl
        INC     BX
        mov     cx,bx
        MOV     BX,alloca               ;base of allocation vector
                                        ;fill the allocation vector
                                        ;with zeros
init0:
        MOV     b[bx],0
        INC     BX                      ;alloc(i)=0
        loop    init0
                                        ;set the reserved space for
                                        ;the directory
        MOV     DX,dirblk
        MOV     BX,alloca               ;BX=.alloc()
        MOV     [bx],dx                 ;sets reserved directory blks
                                        ;allocation vector initialized
        CALL    home                    ;home disk
                                        ;cdrmax = 3 (scans at least
                                        ;one directory record)
        MOV     BX,cdrmaxa
        mov     w[bx],3
                                        ;cdrmax = 0000
        CALL    setenddir               ;dcnt = enddir
                                        ;read directory entries and
                                        ;check for allocated storage
init2:
        MOV     CL,true
        CALL    rddir
        CALL    endofdir
        jz      ret8b                   ;rz
                                        ;not end of directory,
                                        ;valid entry ?
        CALL    getdptra                ;BX = buffa + dptr
        cmp     b[bx],empty
        JZ      init2                   ;go get another item
                                        ;not empty,user code the same?
        MOV     AL,usrcode
        CMP     AL,[bx]
        JNZ     pdollar
                                        ;same user code,
                                        ;check for '$' submit
        INC     BX
        MOV     AL,[bx]                 ;first character
        SUB     AL,'$'                  ;dollar file?
        JNZ     pdollar
                                        ;$ file found, mark in lret
        DEC     AL
        MOV     lret,al                 ;lret = 255
pdollar:
                                        ;now scan the disk map for
                                        ;allocated blocks
        MOV     CL,1                    ;set to allocated
        CALL    scndm
        CALL    setcdr                  ;set cdrmax to dcnt
        jmps    init2                   ;for another entry
;
cpydirloc:
        ;copy directory location to lret following
        ;delete, rename, ... ops
        MOV     AL,dirloc
        JMP     staret
;       ret
;
compext:
        ;compare extent# in AL with that in CL, return nonzero
        ;if they do not match
        PUSH    CX                      ;save CL's original value
        MOV     ch,extmsk
        NOT     ch
                                        ;CH has negated form of
                                        ;extent mask
        and     cl,ch                   ;low bits removed from CL
        AND     AL,CH                   ;low bits removed from AL
        SUB     AL,CL
        AND     AL,maxext               ;set flags
        POP     CX                      ;restore original values
ret8b:  RET
;
srch:
                                        ;search for directory element
                                        ;of lenght
        MOV     dirloc,0ffh             ;changed if actually found
        MOV     srchl,cl                ;searchl = CL
        MOV     BX,info
        MOV     srcha,BX                ;searcha = info
        CALL    setenddir               ;dcnt = enddir
        CALL    home                    ;to start at the beginning
                                        ;(drop through to searchn)                      ;
;
srchn:
        ;search for the next directory element, assuming
        ;a previous call on search which sets searcha and searchhl
        MOV     CL,false
        CALL    rddir                   ;read next dir element
        CALL    endofdir
        jz      srchfin
                                        ;skip to end if so
                                        ;not end of directory,
                                        ;scan for match
        MOV     dx,srcha                ;DX=beginning of user fcb
        MOV     SI,DX
        LODS    al                      ;first character
        CMP     AL,empty                ;keep scanning if empty
        JZ      srchnext
                                        ;not empty, may be end of
                                        ;logical directory
        PUSH    DX                      ;save search address
        CALL    compcdr                 ;past logical end?
        POP     DX                      ;recall address
        jnb     srchfin
                                        ;artificial stop
srchnext:
        CALL    getdptra                ;BX = buffa+dptr
        MOV     AL,srchl
        MOV     CL,AL                   ;length of search to CL
        MOV     CH,0                    ;CH counts up, CL counts down
srchloop:
        or      cl,cl
        JZ      endsearch
        MOV     SI,DX
        LODS    al
        CMP     AL,'?'
        JZ      srchok                  ;? matches all
                                        ;scan next character if not
                                        ;ubytes
        CMP     ch,ubytes
        JZ      srchok
                                        ;not the ubytes field,
                                        ;extent field
        CMP     ch,extnum               ;may be extent field
        MOV     SI,DX
        LODS    al                      ;fcb character
        JZ      srchext                 ;skip to search extent
        SUB     AL,[bx]
        AND     AL,7fh                  ;mask-out flags/extent modulus
        jnz     srchn
        jmps    srchok                  ;matched character
srchext:
                                        ;A has fcb character
                                        ;attempt an extent # match
        PUSH    CX                      ;save counters
        MOV     CL,[bx]                 ;directory character to c
        CALL    compext                 ;compare user/dir char
        POP     CX                      ;recall counters
        jnz     srchn                   ;skip if no match
srchok:
                                        ;current character matches
        INC     DX
        INC     BX
        INC     CH
        DEC     CL
        jmps    srchloop
endsearch:
        ;entire name matches, return dir position
        MOV     AL,ldcnt
        AND     AL,dskmsk
        MOV     lret,al
                                        ;lret = low(dcnt) and 11b
        LEA     BX,dirloc
        MOV     AL,[bx]
        RCL     AL,1
        jae     ret9                    ;rnc
                                        ;yes, change it to 0 to
                                        ;mark as found
        XOR     AL,AL
        MOV     [bx],al                 ;dirloc=0
ret9:   RET
srchfin:
        ;end of directory, or empty name
        CALL    setenddir               ;may be artifical end
        MOV     AL,255
        JMP     staret
;
;
delet:
        ;delete the currently addressed file
        CALL    checkwrite              ;write protected?
        MOV     CL,extnum
        CALL    srch                    ;search through file type
delet0:
                                        ;loop while directory matches
        CALL    endofdir
        jz      ret9
                                        ;set each non zero disk map
                                        ;entry to 0
                                        ;in the allocation vector
                                        ;may be r/o file
        CALL    ckrodir                 ;ro disk error if found
        CALL    getdptra                ;BX=.buff(dptr)
        MOV     b[bx],empty
        MOV     CL,0
        CALL    scndm                   ;alloc elts set to 0
        CALL    wrdir                   ;write the directory
        CALL    srchn                   ;to next element
        jmps    delet0                  ;for another record
;
getblock:
        ;given allocation vector position CX, find the zero bit
        ;closest to this position by searching left and right.
        ;if found, set the bit to 1 and return the bit position
        ;in BX.  if not found (i.e., we pass 0 on the left, or
        ;maxall on the right), return 0000 in BX
;
        MOV     dx,cx                   ;copy start pos. to DX
lefttst:
        or      cx,cx
        JZ      righttst                ;skip if left=0000
                                        ;left not at position zero,
                                        ;bit zero ?
        DEC     CX
        PUSH    DX
        PUSH    CX                      ;left,right pushed
        CALL    getallocbit
        RCR     AL,1
        JAE     rblok                   ;return block number if zero
                                        ;bit is one, so try the right
        POP     CX
        POP     DX                      ;left, right restored
righttst:
        cmp     dx,maxall               ;value of maximum allocation#
        JAE     rblok0                  ;return block 0000 if so
        INC     DX
        PUSH    CX
        PUSH    DX                      ;left, right pushed
        MOV     cx,dx                   ;ready right for call
        CALL    getallocbit
        RCR     AL,1
        JAE     rblok                   ;return block number if zero
        POP     DX
        POP     CX                      ;restore left and right ptr
        jmps    lefttst                 ;for another attempt
rblok:
        RCL     AL,1
        INC     AL                      ;bit back into position
                                        ;and set to 1
                                        ;DL contains the number of
                                        ;shifts required to reposition
        CALL    rotr                    ;move bit back to position
                                        ;and store
        POP     BX
        POP     DX                      ;BX returned value,
                                        ;DX discarded
        RET
rblok0:
                                        ;cannot find an available
                                        ;bit, return 0000
        or      cx,cx
        jnz     lefttst
        MOV     BX,0000h
        RET
;
copyfcb:
                                        ;copy the entire FCB
        MOV     CL,0
        MOV     DL,fcblen               ;start at 0, to fcblen-1
                                        ;       jmp copydir                     ;
;
copydir:
        ;copy fcb information starting
        ;into the currently addressed directory entry
        PUSH    DX                      ;save length for later
        MOV     CH,0                    ;double index to CX
        MOV     dx,info                 ;BX = source for data
        ADD     dx,CX                   ;DX=.fcb(CL), source for copy
        CALL    getdptra                ;BX=.buff(dptr), destination
        POP     CX                      ;DX=source, BX=dest, C=length
        CALL    move                    ;data moved
seekcopy:
        ;enter from close to seek and copy current element
        CALL    seekdir                 ;to the directory element
        JMP     wrdir                   ;write the directory element
                                        ;ret
;
;
rename:
        ;rename the file described by the first half of
        ;the currently addressed file control block. the
        ;new name is contained in the last half of the
        ;currently addressed file conrol block. the file
        ;name and type are changed, but the reel number
        ;is ignored.  the user number is identical.
;
        CALL    checkwrite              ;may be write protected
                                        ;search up to the extent field
        MOV     CL,extnum
        CALL    srch
                                        ;copy position 0
        MOV     BX,info
        MOV     AL,[bx]                 ;BX=.fcb(0), A=fcb(0)
        ADD     BX,dskmap               ;bx=.fcb(diskmap)
        MOV     [bx],al                 ;fcb(dskmap)=fcb(0)
                                        ;assume the same disk
                                        ;for new named file
renam0:
        CALL    endofdir
        JNZ     L_51
ret10:  RET
L_51:                                   ;stop at end of dir
                                        ;not end of directory,
                                        ;rename next element
        CALL    ckrodir                 ;may be read-only file
        MOV     CL,dskmap
        MOV     DL,extnum
        CALL    copydir
                                        ;element renamed, move to next
        CALL    srchn
        jmps    renam0
;
indicators:
        ;set file indicators for current fcb
        MOV     CL,extnum
        CALL    srch                    ;through file type
indic0:
        CALL    endofdir
        jz      ret10                   ;stop at end of dir
                                        ;not end of directory,
                                        ;continue to change
        MOV     CL,0
        MOV     DL,extnum               ;copy name
        CALL    copydir
        CALL    srchn
        jmps    indic0
;
open:
        ;search for the directory entry, copy to FCB
        MOV     CL,namlen
        CALL    srch
        CALL    endofdir
        jz      ret10                   ;return with lret=255 if end
                                        ;not end of directory,copy fcb
opencopy:
                                        ;(referenced below to copy fcb
        CALL    getexta

        PUSH    AX
        PUSH    BX                      ;save extent#
        CALL    getdptra
        XCHG    BX,DX                   ;DX = .buff(dptr)
        MOV     BX,info                 ;BX=.fcb(0)
        MOV     CL,nxtrec               ;length of move operation
        PUSH    DX                      ;save .buff(dptr)
        CALL    move                    ;from .buff(dptr) to .fcb(0)
                                        ;note that entire fcb is
                                        ;copied, including indicators
        CALL    setfwf                  ;sets file write flag
        POP     DX
        MOV     BX,extnum
        ADD     BX,DX                   ;bx=.buff(dptr+extnum)
        mov     cl,[bx]

        MOV     BX,reccnt
        ADD     BX,DX
        mov     ch,[bx]

        POP     BX
        POP     AX
        MOV     [bx],al                 ;restore extent number
;
        ;BX = .user extent#, CH = dir rec cnt, CL = dir extent#
        ;if user ext < dir ext then user := 128 records
        ;if user ext = dir ext then user := dir records
        ;if user ext > dir ext then user := 0 records
;
        CMP     cl,[bx]
        MOV     AL,CH                   ;ready dir reccnt
        JZ      openrcnt                ;if same, user gets dir reccnt
        MOV     AL,0
        JB      openrcnt                ;user is larger
        MOV     AL,128                  ;directory is larger
openrcnt:                               ;A has record count to fill
        MOV     BX,info
        MOV     DX,reccnt
        ADD     BX,DX
        MOV     [bx],al
ret11:  RET
;
mergezero:
                                        ;BX = .fcb1(i), DX = .fcb2(i),
                                        ;if fcb1(i) = 0
                                        ;then fcb1(i) := fcb2(i)
        cmp     w[bx],0
        jnz     ret11                   ;rnz
        MOV     SI,DX
        LODS    ax
        mov     [bx],ax
ret12:  RET
;
close:
        ;locate the directory element and re-write it
        XOR     AX,AX
        MOV     lret,al
        MOV     dcnt,ax
        CALL    nowrite
        jnz     ret12                   ;skip close if r/o disk
                                        ;check file write flag -
                                        ;0 indicates written
        CALL    getmodnum               ;fcb(modnum) in A
        AND     AL,fwfmsk
        jnz     ret12                   ;return if bit remains set
        MOV     CL,namlen
        CALL    srch                    ;locate file
        CALL    endofdir
        jz      ret12                   ;return if not found
                                        ;merge the disk map at info
        MOV     CX,dskmap
        CALL    getdptra
        ADD     BX,CX
        XCHG    BX,DX                   ;DX is .buff(dptr+16)
        MOV     BX,info
        ADD     BX,CX                   ;dx=.buff(dptr+16),
                                        ;bx=.fcb(16)
        MOV     CL,(fcblen-dskmap)      ;length of single byte dm
merge0:
        cmp     single,0
        JZ      merged                  ;skip to double
;
        ;this is a single byte map
        ;if fcb(i) = 0 then fcb(i) = buff(i)
        ;if buff(i) = 0 then buff(i) = fcb(i)
        ;if fcb(i) <> buff(i) then error
;
        MOV     AL,[bx]
        OR      AL,AL
        MOV     SI,DX
        LODS    al
        JNZ     fcbnzero
                                        ;fcb(i) = 0
        MOV     [bx],al                 ;fcb(i) = buff(i)
fcbnzero:
        OR      AL,AL
        JNZ     buffnzero
                                        ;buff(i) = 0
        MOV     AL,[bx]
        MOV     DI,DX
        stos    al                      ;buff(i)=fcb(i)
buffnzero:
        CMP     AL,[bx]
        jnz     mergerr                 ;fcb(i) = buff(i)?
        jmps    dmset                   ;if merge ok
merged:
                                        ;this is a double byte merge
        CALL    mergezero               ;buff = fcb if buff 0000
        XCHG    BX,DX
        CALL    mergezero
        XCHG    BX,DX                   ;fcb = buff if fcb 0000
                                        ;they should be identical
                                        ;at this point
        MOV     SI,DX
        mov     ax,[si]
        cmp     ax,[bx]
        jnz     mergerr
        inc     dx
        inc     bx
                                        ;merge ok for this pair
        DEC     CL                      ;extra count for double byte
dmset:
        INC     DX
        INC     BX
        DEC     CL
        JNZ     merge0                  ;for more
                                        ;end of disk map merge,
                                        ;check record count
                                        ;DX = .buff(dptr)+32,
                                        ;BX = .fcb(32)
        MOV     CX,-(fcblen-extnum)
        ADD     BX,CX
        XCHG    BX,DX
        ADD     BX,CX
                                        ;DX = .fcb(extnum),
                                        ;BX = .buff(dptr+extnum)
        MOV     SI,DX
        LODS    al                      ;current user extent number
                                        ;if fcb(ext) >= buff(fcb) then
                                        ;buff(ext) := fcb(ext),
                                        ;buff(rec) := fcb(rec)
        CMP     AL,[bx]
        JB      endmerge
                                        ;fcb extent number >=
                                        ;dir extent number
        MOV     [bx],al                 ;buff(ext) = fcb(ext)
                                        ;update dir. rec count field
        MOV     CX,(reccnt-extnum)
        ADD     BX,CX
        XCHG    BX,DX
        ADD     BX,CX
                                        ;DX=.buff(reccnt),
                                        ;BX=.fcb(reccnt)
        MOV     AL,[bx]
        MOV     DI,DX
        stos    al                      ;buff(reccnt)=fcb(reccnt)
endmerge:
        MOV     fcbcopied,true          ;mark as copied
        JMP     seekcopy                ;ok to "wrdir" here -
                                        ;1.4 compatible
                                        ; ret
mergerr:
                                        ;elements did not merge ok
        dec     lret                    ;=255 non zero flag set
ret13:  RET
;
make:
        ;create a new file by creating a directory entry
                                        ;then opening the file
        CALL    checkwrite              ;may be write protected
        push    info
        MOV     info,offset efcb        ;info = .empty
        MOV     CL,1
        CALL    srch                    ;length 1 match on empty entry
        CALL    endofdir                ;zero flag set if no space
        pop     info                    ;recall word ptr info address
        jz      ret13                   ;return with error condition
                                        ;255 if not found
        XCHG    BX,DX                   ;DX = word ptr info address
                                        ;clear the remainder of fcb
        MOV     BX,namlen
        ADD     BX,DX                   ;bx=.fcb(namlen)
        MOV     CL,fcblen-namlen        ;number of bytes to fill
        XOR     AL,AL                   ;clear AL for fill
make0:
        MOV     [bx],al
        INC     BX
        DEC     CL
        JNZ     make0
        MOV     BX,ubytes
        ADD     BX,DX   ;bx=.fcb(ubytes)
        MOV     [bx],al                 ;fcb(ubytes) = 0
        CALL    setcdr                  ;may have extended dir
                                        ;now copy entry to dir
        CALL    copyfcb
                                        ;set file write flag to "1"
        JMP     setfwf
                                        ;ret
;
openreel:
        ;close the current extent, and open the next one
        ;if possible.  RMF is true if in read mode.
        XOR     AL,AL
        MOV     fcbcopied,AL            ;set true if actually copied
        CALL    close                   ;close current extent
                                        ;lret remains at enddir
                                        ;if we cannot open next ext
        CALL    endofdir
        jz      ret13                   ;return if end
                                        ;increment extent number
        MOV     BX,info
        ADD     BX,extnum               ;BX=.fcb(extnum)
        MOV     AL,[bx]
        INC     AL
        AND     AL,maxext
        MOV     [bx],al                 ;fcb(extnum)=++1
        JZ      openmod                 ;move to next module if 0
                                        ;may be in same extent group
        MOV     CH,AL
        MOV     AL,extmsk
        AND     AL,CH
                                        ;if 0, then not in same group
        and     al,fcbcopied            ;true if fcb was copied to dir
                                        ;puts 0 in AL if not written
        JZ      openr0                  ;go to next physical extent
                                        ;result is non zero,
                                        ;so we must be in same extent
        jmps    openr1                  ;to copy fcb info
openmod:
        ;extent number overflow, go to next module
        ADD     BX,(modnum-extnum)      ;bx=.fcb(modnum)
        INC     b[bx]                   ;fcb(modnum)=++1
                                        ;module number incremented,
                                        ;check for overflow
        MOV     AL,[bx]
        AND     AL,maxmod               ;mask high order bits
        JZ      openerr                 ;cannot overflow to 0
                                        ;otherwise, ok to continue
                                        ;with new module
openr0:
        MOV     CL,namlen
        CALL    srch                    ;next extent found?
        CALL    endofdir
        JNZ     openr1
                                        ;end of file encountered
        MOV     AL,rmf
        INC     AL                      ;0ffh becomes 00 if read
        JZ      openerr                 ;sets lret = 1
                                        ;try to extend current file
        CALL    make
                                        ;cannot be end of directory
        CALL    endofdir
        JZ      openerr                 ;with lret = 1
        jmps    openr2
openr1:
                                        ;not end of file, open
        CALL    opencopy
openr2:
        CALL    getfcb                  ;set parameters
        XOR     AL,AL
        JMP     staret                  ;lret = 0
                                        ; ret
openerr:
                                        ;cannot move to next extent
                                        ;of this file
        CALL    setlret1                ;lret = 1
        JMP     setfwf                  ;ensure that it will not
                                        ;be closed
                                        ;ret
;
;*************** end BDOS file system part 2 ***************
end
