title 'DIRS - dos directory support'
;    File              : $DIRS.A86$
;
;    Description       :
;
;    Original Author   : DIGITAL RESEARCH
;
;    Last Edited By    : $CALDERA$
;
;-----------------------------------------------------------------------;
;    Copyright Work of Caldera, Inc. All Rights Reserved.
;      
;    THIS WORK IS A COPYRIGHT WORK AND CONTAINS CONFIDENTIAL,
;    PROPRIETARY AND TRADE SECRET INFORMATION OF CALDERA, INC.
;    ACCESS TO THIS WORK IS RESTRICTED TO (I) CALDERA, INC. EMPLOYEES
;    WHO HAVE A NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF
;    THEIR ASSIGNMENTS AND (II) ENTITIES OTHER THAN CALDERA, INC. WHO
;    HAVE ACCEPTED THE CALDERA OPENDOS SOURCE LICENSE OR OTHER CALDERA LICENSE
;    AGREEMENTS. EXCEPT UNDER THE EXPRESS TERMS OF THE CALDERA LICENSE
;    AGREEMENT NO PART OF THIS WORK MAY BE USED, PRACTICED, PERFORMED,
;    COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED, ABRIDGED,
;    CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,
;    TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF
;    CALDERA, INC. ANY USE OR EXPLOITATION OF THIS WORK WITHOUT
;    AUTHORIZATION COULD SUBJECT THE PERPETRATOR TO CRIMINAL AND
;    CIVIL LIABILITY.
;-----------------------------------------------------------------------;
;
;    *** Current Edit History ***
;    *** End of Current Edit History ***
;
;    $Log$   
;    DIRS.A86 1.13 94/12/01 13:16:24
;    changed error code if directory entry cannot be allocated;    
;    DIRS.A86 1.12 93/08/27 18:49:04
;    hash code fixup on previously unused entries resets hash count
;    pcformat bug where an extra (zero-length) command.com was left on disk
;    ENDLOG
;
;	Date	   Who	Modification
;	---------  ---	---------------------------------------
;   19 Aug 91 Initial version created for VLADIVAR

eject

include	bdos.equ
include i:mserror.equ
include	i:fdos.equ

eject
PCMODE_DATA	dseg
if DELWATCH
	extrn	fdos_stub:dword		; for calling delwatch TSR
endif

BDOS_DATA	dseg	word
	extrn	adrive:byte
	EXTRN	clsize:WORD
	extrn	diradd:word
	extrn	dirinroot:word
	EXTRN	dirperclu:WORD
	EXTRN	dosfat:WORD
	extrn	hashroot:dword
	extrn	hashmax:word
	EXTRN	info_fcb:BYTE
	extrn 	lastcl:word
	extrn	psecsiz:word

eject

hash		rw	2		; hash code work area

; The dirbcb says what is in the local dirbuf

dirbcb		db	0ffh		; drive of dirbuf entry
dirbcb_cl	dw	0		; cluster of dirbuf entry
dirbcb_dcnt	dw	0		; directory index of dirbuf entry
dirbcb_block	rw	2		; block of dirbuf entry
dirbcb_offset	dw	0		; byte offset in block of dirbuf entry


	public	dirbuf
dirbuf		rb	32		; local directory buffer

	public	dirp
dirp		dw	0		; directory entry pointer

	public	dcnt
dcnt	dw	0			; directory index count

	public	finddfcb_mask
finddfcb_mask	dw	0800h		; hi byte = reject DA_VOLUME attribs
					; lo byte = accept non-0 start clusters
					; 00FF = include labels, but not
					;	pending deletes
					; 0000 = include everything
	public	chdblk
chdblk		dw	0		; current cluster # of directory


BDOS_CODE	cseg
	extrn	alloc_cluster:NEAR
	extrn	clus2sec:near
	extrn	hdsblk:near		; get current directory block
	extrn	fdos_error:NEAR
	extrn	fixfat:NEAR
	extrn	getnblk:NEAR
	extrn	locate_buffer:near
	extrn	update_dir:NEAR
	extrn	update_fat:NEAR
	extrn	zeroblk:near

eject


	public	allocdir
	public	discard_dirbuf
	public	finddfcb
	public	finddfcbf
	public	fill_dirbuf
	public	flush_dirbuf
	public	getdir
	public	hshdscrd
	public	mkhsh
	public	setenddir
eject

;----------
fill_dirbuf:	;get 32 byte directory entry
;----------
; On Entry:
;	AX = cluster to read (0=root)
;	BX = dir within cluster
; On Exit:
;	DI -> dirbuf entry

	call	discard_dirbuf		; invalidate block in case of error
	mov	dirbcb_cl,ax		; remember which cluster
	mov	dirbcb_dcnt,bx		;  and dir entry we want
	test	ax,ax			; are we in the root ?
	 jz	fill_dirbuf10
	mov	cl,FCBSHF
	shl	bx,cl			; BX = byte offset in cluster
	call	clus2sec		; DX:AX -> sector
	jmps	fill_dirbuf20		; BX = offset in sector
fill_dirbuf10:
	mov	ax,FCBLEN
	mul	bx			; DX:AX = byte offset
	div	psecsiz			; AX = sector offset, DX = byte offset
	mov	bx,dx			; BX = byte offset in sector
	xor	dx,dx
	add	ax,diradd		; add in start of root dir
	adc	dx,dx
fill_dirbuf20:
	mov	dirbcb_block,ax		; we want this sector
	mov	dirbcb_block+WORD,dx
	mov	dirbcb_offset,bx
	xchg	ax,dx			; DX = low word of sector
	mov	ah,al			; AH = low byte of high word
	push	bx			; save byte offset in sector
	mov	cx,0FF00h+BF_ISDIR	; locate directory sector
	call	locate_buffer		; ES:SI -> BCB_
	pop	bx			; BX = offset within sector
	push es ! pop ds		; DS:SI -> buffer control block
	lea	si,BCB_DATA[si+bx]	; DS:SI -> data in buffer
	push ss ! pop es
	mov	di,offset dirbuf	; ES:DI -> dir buffer
	push	di
	mov	cx,32/WORD		; copy into local buffer
	rep	movsw
	pop	di			; DI -> dir buffer
	push ss ! pop ds
	mov	al,adrive		; remember where we are
	mov	dirbcb,al		;  so we can write it back
	ret


;------------
flush_dirbuf:
;------------
	mov	al,0FFh
	xchg	al,dirbcb		; do we have anything to flush ?
	cmp	al,adrive
	 jne	flush_dir20		; skip if invalid contents
	mov	si,offset dirbcb_block
	lodsw				; get low word of block
	xchg	ax,dx			; put it in DX where it belongs
	lodsw				; get high word of block
	mov	ah,al			; AH:DX -> block to find
	mov	cx,0FF00h+BF_ISDIR	; look for directory
	call	locate_buffer		; locate physical sector
	or	es:BCB_FLAGS[si],BF_DIRTY; mark this buffer as modified
	mov	bx,dirbcb_offset	; BX = offset within buffer
	lea	di,BCB_DATA[si+bx]	; ES:DI -> offset in buffer

	mov	al,es:[di]		; AL = 1st character of dir entry

	mov	si,offset dirbuf	; get CP/M buffer address
	mov	cx,32/WORD
	rep	movsw			; copy modified entry back

	push	ax
	xor	dh,dh			; we only want HCB_ if it's there
	mov	cx,dirbcb_cl		;  and it's this cluster
	call	find_hcb		; does an HCB_ exist for this entry ?
	pop	ax
	 jc	flush_dir20		; no, skip update
	mov	di,dirbcb_dcnt		; we want this dir entry
	cmp	di,es:HCB_CNT[bx]	; is this within the hashed entries ?
	 jae	flush_dir20		;  no, skip the fixup

	test	al,al			; are we using a never used entry ?
	 jnz	flush_dir10		; if so don't trust subsequent hash
	inc	di			;  codes as they have never been read.
	mov	es:HCB_CNT[bx],di	; Truncate table to force a read of the
	dec	di			;  next dir entry (which will normally
flush_dir10:				;  also be never used)
	shl	di,1			; DI = offset of hashed entry
	lea	di,HCB_DATA[bx+di]
	mov	si,offset dirbuf	; this is the dir entry
	call	mkhsh			; AX = hash code of our entry
	stosw				; update hash code for dir entry
flush_dir20:
	push	ds ! pop es		; ES = local data segment
	ret

;--------------
discard_dirbuf:
;--------------
	mov	dirbcb,0FFh		; invalidate dirbuf
	ret


;--------
rd_pcdir:
;--------
;	Exit:	AX = offset of directory entry
;		   = 0 if end of directory


	mov	bx,dcnt
	inc	bx
	mov	dcnt,bx		; dcnt=dcnt+1
	call	hdsblk		; AX = current directory block
	 jz	rd_pcdir40	; skip if we're at the root

; we we in a subdirectory - lets follow the chain

	xchg	ax,cx		; keep subdir cluster in CX
	mov	ax,FCBLEN	; AX = size of dir entry
	mul	bx		; DX:AX = offset of set entry we want
	div	clsize		; AX = # clusters to skip, DX = offset in cluster
	xchg	ax,dx		; DX = # to skip, AX = offset in cluster
	xchg	ax,cx		; AX = start of chain, CX = offset in cluster
	xchg	bx,cx		; BX = offset in cluster, CX = dcnt
	 jcxz	rd_pcdir20	; 1st subdir entry, we are already there
	mov	cx,chdblk	; do we already know where we are ?
	 jcxz	rd_pcdir10	;  if not trace from start of chain
	xchg	ax,cx		; AX = cluster of last dir entry
	test	bx,bx		; have we moved onto next cluster?
	 jnz	rd_pcdir20	; no, trust me..
	mov	dx,1		; move on to next entry in the chain
rd_pcdir10:
	or	dx,dx		; skip along chain until we arrive
	 jz	rd_pcdir20	;  at the destination cluster
	dec	dx
	push	bx
	push	dx
	call	getnblk		; AX = next cluster in chain
	pop	dx
	pop	bx
	cmp	ax,lastcl	; have we fallen off the end of the chain ?
	 jbe	rd_pcdir10
	jmps	rd_pcdir30	; yes, set end of directory
rd_pcdir20:
	mov	chdblk,ax	; remember this cluster for next time
	mov	cl,FCBSHF	; to divide by fcb size
	shr	bx,cl		; BX = dir offset in cluster
	jmps	rd_pcdir50	;  now go and find the entry

rd_pcdir30:
	call	setenddir	; yes, set dcnt to end of directory
	jmps	rd_pcdir60

rd_pcdir40:
; we are in the root directory
	cmp	bx,dirinroot	; end of the root directory ?
	 jae	rd_pcdir30
rd_pcdir50:
	call	fill_dirbuf	;locate directory entry
	xchg	ax,di		; AX -> dir entry
	cmp	dcnt,ENDDIR
	 jnz	rd_pcdir70
rd_pcdir60:
	xor	ax,ax		; return 0 if endofdir
rd_pcdir70:
	mov	bx,ax
	ret


;---------
setenddir:	;set dcnt to the end of directory (dcnt = 0ffffh)
;---------
	mov	dcnt,ENDDIR
	mov	chdblk,0
	ret


chk_wild:	;check fcb for ? marks
;--------
; On Entry:
;	bx -> FCB
; On Exit:
;	ZF set if ? found
;	BX preserved
	push	ds ! pop es		; ES -> SYSDAT
	lea	di,byte ptr FNAME[bx]	; ES:DI -> name to scan
	mov	cx,11
	mov	al,'?'			; scan for wild cards
	repne	scasb
	ret

eject

eject
;---------
finddfcbf:	; Find matching directory fcb(dfcb) from beginning of directory
;---------
	call	setenddir	; set up for search first

;--------
finddfcb:	; Find matching directory fcb(dfcb)
;--------
	mov	cx,2

;------
getdir:
;------
;	entry:	CH  =	offset info_fcb	(always 0 except from rename)
;		CL  = 	search length
;			0 = return next fcb
;			1 = return empty fcb
;			2 = find match  (Based on info_fcb)
;			3 = find match?  Based on info_fcb
;
;	exit:	AX,BX,DIRP = pointer to dfcb
;			     0 = no match (end of directory)
;			     other = offset of requested directory entry
;		ZF = zero flag is set based on AX
;

;	Note:	The most common call for this function is with CX =
;		2 (match with name, not extent)  with 'dcnt' set to
;		0FFFFh  (search  from  beginning  of the  directory
;		(e.g.   open,  create,   delate,   rename,   etc.).
;		Therefore  we try  to optimize  directory  searches
;		using a dynamic hash table...

					;struct dirfcb *getdir(offset,srchl);

	cmp	dcnt,0FFFFh		;if ((dcnt == 0xffff) &&
	 jne	gtd_next
	mov	hash+2,cx		; Save off calling option
	xor	ax,ax			; hash code 0 for free entry
	cmp	cx,1			; what kind of search?
	 je	gtdo15			; CL=1: find free entry (AX=0)
	 jb	gtd_next		; CL=0: find any entry (unhashed)
	or	ch,ch			; name in INFO_FCB+1?
	 jnz	gtd_next		; no, unhashed search
	mov	bx,offset info_fcb
	call	chk_wild		; wildcards used in search?
	 jz	unhshd1			; yes, can't use hashing
	mov	si,offset info_fcb+1	; else compute hash code
	call	mkhsh			;    for name to find
gtdo15:
	mov	hash,ax			; save it for search
	call	hdsblk			; get directory block
gtdo3:
	push	ax			; save dir block for later
	call	hashsrch		; try and use hashing to find a match
	 jnc	gtdo4			; look closer if we get possible match
	add	dcnt,ax			;  else skip known non-matches
	pop	ax			; recover current dir block
	test	ax,ax			; if we are in the root
	 jz	unhashed		;  we must search the hard way
	xchg	ax,bx
	mov	ax,dcnt			; should we go onto next cluster ?
	inc	ax			; only if next entry is the start
	xor	dx,dx			;  of a cluster
	div	dirperclu
	xchg	ax,bx
	test	dx,dx			; at start of cluster ?
	 jnz	unhashed
	call	getnblk			; onto next cluster until we are
	cmp	ax,lastcl		;  at the end of the chain
	 jbe	gtdo3
	jmps	unhashed		; out of luck
gtdo4:
	add	dcnt,ax			; we have found a match, so start
	pop	ax			;  search here
;	jmps	unhashed
unhashed:				;   /* locate entry */
	mov	chdblk,0
unhshd1:
	mov	cx,hash+2		;}
gtd_next:
;--------
	push	cx
	call	rd_pcdir		; Get Next DFCB
	pop	cx
gtd_exit:
	mov	dirp,ax			; assume this is the one
	mov	bx,ax
	or	ax,ax			; should we exit with not found ?
	 jz	gtd2
	cmp	cl,NEXT			; Caller wishes next dfcb?
	 jne	gtd3			; NO
gtd2:
	mov	ax,bx			; return BX (DIRP or NULLPTR)
	or	ax,ax			; return ZF (1 = not found)
	ret

gtd3:
	cmp	cl,EMPTY		; Caller wishes an empty dfcb?
	 jne	gtd4			; NO
	mov	al,DNAME[bx]		; Get directory type
	or	al,al			; Is it free?
	 jz	gtd2			; YES		(00 -> never used)
	cmp	al,0E5h			; Is the dfcb empty?
	 je	gtd2			; YES		 (E5 -> erased)
	jmps	gtd_next		; NO, try the next

gtd4:					; looking for particular entry
	call	hdsblk			; Are we at the root?
	 jnz	gtd5			; skip if not
	mov	ax,dcnt			; check for end of directory
	cmp	ax,dirinroot		; have we reached end of root?
	mov	ax,0			; assume we have
	 jae	gtd_exit		; exit if we have
gtd5:
	mov	al,DNAME[bx]		; Get dfcb type
	cbw
	or	ax,ax			; Are we at End Of Directory(EOD)
	 jz	gtd_exit		; YES
	cmp	al,0E5h			; Is this a free fcb?
	 je	gtd_next		; Yes, try again
	mov	ax,finddfcb_mask	; do we want labels/pending deletes
	test	DATTS[bx],ah		; filter out volume labels?
	 jnz	gtd_next		;  we normally reject them
if DELWATCH
	cbw				; we want labels - do we want
	test	word ptr DBLOCK1[bx],ax	;  DELWATCH pending deletes
	 jnz	gtd_next		;  ie. labels with fat chain
endif
	push	cx			; we are interested - but does
	mov	al,ch			;  the name match ?
	cbw
	add	ax,offset info_fcb+1
	xor	si,si			; we want SI = entry to match and
	xchg	ax,si			;   AL = 0 indicating assumed match
	mov	cx,11			; 11 chars in filename
	mov	di,bx			; ES:DI -> directory entry
match3:
	 jcxz	match4			; stop if we have done all 11
	repe	cmpsb			; compare if 11 bytes the same
	 je	match4			;  skip if all bytes the same
	cmp	byte ptr 0-1[si],'?'	; else was INFO_FCB byte = '?'
	 je	match3			;  in that case it matches too
	inc	ax			; else we didn't match (AL<>0)
match4:
	pop	cx
	or	al,al			; did we match ?
	 jnz	gtd_next		; no, try for another
	mov	bx,dirp			; Return (BX)
	jmp	gtd2


eject
find_hcb:				; find HCB_ for given drive
;--------
; On Entry:
;	CX = cluster we are looking for
;	DH = 00 if exact match required
;	     FF if we want to recyle oldest HCB_
; On Exit:
;	CY set, AX=0 if HCB_ not found
;	CY clear ES:BX = offset of HCB_ (moved to head of list)
;	(AX/DX trashed, All other regs preserved)
;

	les	bx,hashroot		; get our hashing pointer
	mov	ax,es
	or	ax,bx			; is hashing enabled ?
	 jz	find_hcb30
	mov	dl,adrive		; look for this drive
	cmp	cx,es:HCB_CLU[bx]	; does cluster match?
	 jne	find_hcb10		; goto next if not
	cmp	dl,es:HCB_DRV[bx]	; does drive match?
	 jne	find_hcb10		; goto next if not
;	clc
	ret				; we have a match on the 1st one

find_hcb10:
; no match, so look futher along the chain
	mov	ax,es:HCB_LINK[bx]	; onto the next entry
	test	ax,ax			; is there one ?
	 jz	find_hcb20
	xchg	ax,bx			; AX = previous entry, BX = current
	cmp	cx,es:HCB_CLU[bx]	; does cluster match?
	 jne	find_hcb10		; goto next if not
	cmp	dl,es:HCB_DRV[bx]	; does drive match?
	 jne	find_hcb10		; goto next if not
; we have a match, but it's not the first so recycle it
	mov	dx,es:HCB_LINK[bx]	; get link to the rest of the chain
	xchg	ax,bx			; BX = previous entry
	mov	es:HCB_LINK[bx],dx	; unlink ourselves from chain
	mov	bx,ax			; BX = current entry
	xchg	ax,word ptr hashroot	; put current entry at the head
	mov	es:HCB_LINK[bx],ax	;  and relink the rest of the chain
;	clc
	ret

find_hcb20:
; we have been all along the chain with no luck
	xor	ax,ax
	test	dh,dh			; no HCB_ - do we want to recyle ?
	 jz	find_hcb30		;  if not skip
	mov	es:HCB_CNT[bx],ax	; we need to recycle oldest HCB_
	mov	es:HCB_CLU[bx],cx	;  so mark as us, but with nothing
	mov	es:HCB_DRV[bx],dl	;  in it
;	clc
	ret

find_hcb30:
	stc				; return failure
	ret


eject
;-----
mkhsh:
;-----
;
;	entry:	SI = 11 byte FCB to convert to hash code
;	exit:	AX = 1..FFFF is hash code (00/E5 == 0)
;	uses:	DX
;	saves:	BX,CX,DI,BP
;
;	used for hashing the INFO_FCB &
;	directory entries for DOS media

	xor	dx,dx			;assume hash code is 0000
	lodsb
	cmp	al,0E5h			;if deleted file
	 je	mkhsh2			;   or
	cmp	al,0			;if virgin entry
	 je	mkhsh2			;then hash code = 0;
	push	cx			;else save CX
	and	al,7fh
	mov	dh,al			;initialize hash code MSB
	mov	cx,10			;involve other 10 characters
mkhsh1:
	lodsb				;get next character
	rol	dx,1			;rotate hash code by one bit
	and	al,7fh			;strip top bit off character
	xor	dl,al			;XOR the character into the hash code
	loop	mkhsh1			;repeat for all characters
	pop	cx			;restore CX
	test	dx,dx			;test if zero by any chance
	 jnz	mkhsh2			;skip if non-zero
	inc	dx			;else force it to 1
mkhsh2:					;return hash code in AX
	xchg	ax,dx
	ret


eject
if DELWATCH
	Public	fixup_hashing
;
; update hashing for current drive if DELWATCH changes a directory entry
;
fixup_hashing:
;-------------
; On Entry:
;	AX =	segment of dir buffer
;	CX =	cluster to fixup (0 = root)
;	DI =	directory entry index (clipped to cluster if subdir)
;	AX:SI->	dir entry (single entry for hashing)
;
; On Exit:
;	None
;
	push	ds
	push	es

	xor	dh,dh			; we only want HCB_ if it's there
	push	ax			; save seg of dir entry
	call	find_hcb		; does an HCB_ exist for this entry ?
	pop	ds			; DS:SI -> entry to hash
	 jc	fixup_ck10		; not hashed, skip update
	cmp	di,es:HCB_CNT[bx]	; is this within the hashed entries ?
	 jae	fixup_ck10		;  no, skip the fixup
	call	mkhsh			; AX = hash code of our entry

	shl	di,1			; DI = offset of hashed entry
	lea	di,HCB_DATA[bx+di]
	stosw				; update hash code for dir entry

fixup_ck10:
	pop	es
	pop	ds
	ret				; no
endif


eject

hashsrch:
;--------
;	entry:	AX = starting cluster of directory
;	exit:	AX is possible match index
;
	mov	dh,0FFh			; we want HCB_ even if it's recycled
	xchg	ax,cx			;  and this block
	call	find_hcb		; does an HCB_ exist for this entry ?
;	mov	ax,0			; assume unhashed search required
	 jc	hashsrch20		;  start one if no hashing
hashsrch10:
	mov	cx,es:HCB_CNT[bx]	; we have this many entries hashed
	 jcxz	hashsrch30		; skip if nothing hashed yet
	mov	ax,hash			; look for this hash code
	lea	di,HCB_DATA[bx]		; DI = offset of start of search
	repne	scasw			; try to find a match
	 jne	hashsrch30		; skip if no match found
	lea	ax,HCB_DATA+2[bx]	; find word offset of match
	xchg	ax,di			; return matching index
	sub	ax,di
	shr	ax,1			; make dir offset
hashsrch20:
	push ds ! pop es
	clc				; we have found it
	ret

hashsrch30:
	call	rehash_entry		; try and hash another entry
	 jnc	hashsrch10		;  look again if we succeeded

	mov	ax,es:HCB_CNT[bx]	; failure, so return # to skip
	push ds ! pop es
;	stc				;  for quicker search
	ret


rehash_entry:
;------------
;	entry:	ES:BX -> HCB
;		AX = hash cluster number

	call	hash_entries_to_do	; how many entries still to hash ?
	 jcxz	rehash_entry40		; if we have hashed them all exit

	push	dcnt			; save directory count

	mov	ax,dcnt			; get previous position
	inc	ax			; we start looking here
	xor	dx,dx
	div	dirperclu		; mask to start of cluster
	mul	dirperclu
	add	ax,es:HCB_CNT[bx]	; skip entries we already have
	dec	ax			; make previous entry BEFORE this
	mov	dcnt,ax
	mov	chdblk,0		; non-sequential access
	cmp	cx,512/32		; don't try reading more than 512 bytes
	 jb	rehash_entry20		;  at a time - then with 512 byte secs
	mov	cx,512/32		;  we only read when we
rehash_entry20:
	push	es
	push	bx			; save hash control pointer
	push	cx			; save # entries to do
	push ds ! pop es		; back to small model
	xor	cx,cx			; return any entry
	call	gtd_next		; unhashed search
	pop	cx			; restore # entries to do
	pop	bx			; restore hash control pointer
	pop	es
	test	ax,ax			; anything found
	 jz	rehash_entry30		; end of directory
	xchg	ax,si			; else get directory pointer
	mov	di,es:HCB_CNT[bx]
	shl	di,1			; DI -> 1st new entry
	lea	di,HCB_DATA[bx+di]
	push	si
	call	mkhsh			; else calculate hash into AX
	stosw				; add it to hash table
	inc	es:HCB_CNT[bx]		; remember we did
	pop	si
	lodsb				; get 1st byte of hashed entry
	test	al,al			; is it zero (ie. never used)?
	loopne	rehash_entry20		; get all hash codes
	 jcxz	rehash_entry30		; all done ?
	call	hash_entries_to_do	; how many entries still to hash ?
	add	es:HCB_CNT[bx],cx	;  we will do them all..
	rep	stosw			; zap rest of cluster
rehash_entry30:	
	pop	dcnt			; restore count
	mov	chdblk,0		; non-sequential access
	clc				; we have new hashing codes
	ret				; HCB updated with new cluster

rehash_entry40:
	stc				; cannot hash no more...
	ret

hash_entries_to_do:
;------------------
; On Entry:
;	ES:BX -> HCB_
; On Exit:
;	CX = maximum possible entries we still need to hash for HCB_
;	(All other regs preserved)
;
	mov	cx,dirinroot		; assume root dir
	cmp	es:HCB_CLU[bx],0	; was it ?
	 je	hash_etd10
	mov	cx,dirperclu		; subdir, so cluster limit
hash_etd10:
	cmp	cx,hashmax		; do we support this many ?
	 jb	hash_etd20		;   yes, skip it
	mov	cx,hashmax		; else limit it to this many
hash_etd20:
	sub	cx,es:HCB_CNT[bx]	; subtract number we have already done
	ret



eject
hshdscrd:
;--------
;	purge hash blocks for physical drive
;	On Entry:
;		AL = drive to discard (FF = all drives)
;	On Exit:
;		None (All regs preserved)

	push	ds
	push	bx
	lds	bx,hashroot		; get root of hash codes
hshdsc1:
	test	bx,bx
	 jz	hshdsc4			; all blocks done
	cmp	al,0FFh			; FF means discard all drives
	 je	hshdsc2
	cmp	al,ds:HCB_DRV[bx]	; check if matching drive
	 jne	hshdsc3
hshdsc2:
	mov	ds:HCB_DRV[bx],0ffh	;	h->hd = 0xff;
hshdsc3:
	mov	bx,ds:HCB_LINK[bx]	; get next hash code block
	jmps	hshdsc1
hshdsc4:
	pop	bx
	pop	ds
	ret


eject

enlarge_root:
if DELWATCH
	mov	ah,DELW_FREERD		; lets ask DELWATCH if it can
	mov	al,adrive		; free a root directory entry
	callf	ss:fdos_stub		;  for this drive
	 jnc	allocdir		; it says it has so try again
endif
allocdir_err:
	pop	ax			; discard return address
	mov	ax,ED_MAKE
	jmp	fdos_error		; return "cannot make dir entry"


;--------
allocdir:			; Called by rename and MAKE
;--------
	call	setenddir		; search for first match
	mov	cx,1			; return empty fcb
	call	getdir			; is there an empty fcb?
	 jz	allocdir10		; if so use that
	ret
allocdir10:
	call	hdsblk			; Are we at the root?
	 jz	enlarge_root		; YES -- Report error(no room)

	; We are in a subdirectory so enlarge it
	; AX has 1st block of subdirectory   NOTE -- AX is never
	; above 'lastcl' on entry.
allocdir20:
	cmp	ax,lastcl		; Are we at end of subdirectory?
	 ja	allocdir30		; YES
	push	ax
	call	getnblk			; NO -- get next block then
        pop	bx
	jmps	allocdir20

allocdir30:
	push	bx			; save last block number
	xchg	ax,bx			; Get a new block (start from old)
	call	alloc_cluster
	pop	bx
	 jc	allocdir_err		; Report Error(no room on disk)
	push	ax			; save new block
	xchg	ax,bx
	call	fixfat			; Update fat (AX,BX) old last block
					;  points to new last block
	pop	ax			; Get new last block
	push	ax
	mov	bx,dosfat		; 12 or 16 bit fat
	call	fixfat			; Update fat (AX,BX)  new last block
					;  has end of cluster marker
	call	update_fat		; Write out to disk
	pop	ax			; Get new last block
	call	zeroblk			; Zero it out
        call	setenddir		; Set up for search first
	mov	cx,1			; Find empty fcb
	jmp	getdir			; Can not return with not found error

	END
