;    File              : $FUNCS.FDO$
;
;    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$;
;    ENDLOG
;	General function include module for FDOS.A86

BDOS_CODE	cseg

	public fdos_entry

;==========
fdos_entry:			; FDOS module entry point
;==========
;	On Entry:
;		DS:DX -> parameter block
;	On exit:
;		AX = BX = return code
;		(DS/ES corrupted)
;	entry:	DS:DX = argument
;
;	exit:	AX,BX = return code

	mov	si,dx
	lodsw				; AX = FDOS number
	sub	ax,39h			; base it at zero
	 jc	fd_error		; stop if too low
	cmp	ax,FDOS_MAX		; check if function in range
	 jae	fd_error		; yes, continue

	push	ds			; save parameter segment
	push	dx			; save parameter offset
	push	ax			; save sub-function
	mov	bp,sp			; SS:BP -> working variables
	mov	bx,ax
	add	bx,ax
	add	bx,ax
	call	fdos_tbl[bx]
	add	sp,3*WORD		; clean up stack
	mov	ax,bx
	ret

fd_error:
	mov	ax,ED_FUNCTION		; return "invalid function"
	mov	bx,ax
	ret


fdos_tbl	dw	fdos_mkdir	; 39-make directory
		db	2
		dw	fdos_rmdir	; 3A-remove directory
		db	2
		dw	fdos_chdir	; 3B-change directory
		db	2
		dw	fdos_creat	; 3C-create file
		db	4
		dw	fdos_open	; 3D-open file
		db	4
		dw	fdos_close	; 3E-close file
		db	1
		dw	fdos_read	; 3F-read from file
		db	4
		dw	fdos_write	; 40-write to file
		db	4
		dw	fdos_unlink	; 41-delete file
		db	4
		dw	fdos_lseek	; 42-set file pointer
		db	4
		dw	fdos_chmod	; 43-get/set file attributes
		db	6
		dw	fdos_ioctl	; 44-IOCTL emulation
		db	3
		dw	fdos_dup	; 45-duplicate handle
		db	2
		dw	fdos_fdup	; 46-force duplicate handle
		db	2
		dw	fdos_curdir	; 47-get current directory
		db	3
		dw	fdos_getdpb	;*48*disk information
		db	4
		dw	fdos_flush	;*49*flush buffers
		db	0
		dw	fdos_select	;*4A*drive select
		db	1
		dw	fdos_exec	;*4B*create child PSP
		db	1
		dw	fdos_exit	;*4C*close child PSP
		db	0
		dw	fdos_fcb	;*4D*generic FCB call
		db	5
		dw	fdos_first	; 4E-find first matching file
		db	4
		dw	fdos_next	; 4F-find next matching file
		db	0
		dw	fdos_commit	;*50*commit file
		db	1
		dw	fdos_mknew	;*51*make new file
		db	4
		dw	fdos_lock	;*52*lock/unlock block
		db	6
		dw	fdos_mkddsc	; 53 build DDSC from BPB
		db	4
		dw	fdos_ddio	;*54*Int 25/26 emulation
		db	6
		dw	fdos_expand	;*55*expand file name
		db	6
		dw	fdos_move	; 56-rename file
		db	6
		dw	fdos_dattim	; 57-get/set file name
		db	4
		dw	fdos_reopen_fcb	; 58-FCB reopen support function
		db	5

FDOS_MAX	equ	(offset $ - offset fdos_tbl) / 3

	Public	local_disk

local_disk:	; continue execution within MXdisk semaphore
;----------
;	This function will make local copies of the parameters
;	and continue

	callf	ss:lock_tables
	pop	fdos_addr		; return address
	add	sp,2*WORD		; discard return from call fdos_tbl[bx]
					;    and function #
	pop	si			; get parameter offset
	pop	ax			; get parameter segment
	mov	sp_save,sp		; save for re-entry/errors
	mov	ds,ax			; DS:SI -> paremeter block
fdos_reenter:
	push ss ! pop es		; ES = local data segment
	mov	di,offset fdos_pb	; ES:DI -> local parameter block copy
	lodsw ! stosw			; load subfunction number
	mov	es:fdos_info,si		; save it for BDOS return
	mov	es:fdos_info+WORD,ds	; save parameter segment for return
	mov	bx,ax
	add	bx,ax
	add	bx,ax			; BX = function # * 3
	mov	cl,byte ptr fdos_tbl-(39h*(WORD+BYTE))+WORD[bx]
	mov	ch,0
					; get table of # of parameters
	mov	es:fdos_info+2*WORD,cx	; save the parameter count
	rep	movsw			; copy parameter block
	push es ! pop ds		; DS = SYSDAT
	mov	ioexerr,0FFh		; assume default error code
	mov	rwmode,0

	mov	finddfcb_mask,DA_VOLUME*256

	xor	ax,ax
	mov	fdos_ret,ax		; assume success unless proven otherwise
	call	fdos_addr		; call code following "call local_disk"
	mov	ax,fdos_ret		; get return code, fall thru to exit

	Public	fdos_error

fdos_error:
;----------
; AX = return code
	mov	sp,sp_save		; stack to entry value (for errors)
	mov	si,offset fdos_pb+2	; get local copy
	les	di,dword ptr fdos_info	; get parameter block address
	mov	cx,fdos_info+2*WORD	; get parameter block size
	rep	movsw			; copy return values
	mov	bx,ax
	callf	ss:unlock_tables
	ret				; return to BDOS, which copies BX to AX

	Public	fdos_restart

fdos_restart:	; we want to restart the function after an error
;------------	; we already have MXdisk
;
	mov	sp,sp_save		; break out from low levels
	lds	si,dword ptr fdos_info
	dec	si ! dec si		; DS:SI -> parameter block
	jmps	fdos_reenter		; try from scratch

fdos_ED_ROOM:
;-----------
	mov	ax,ED_ROOM		; no more files
	jmps	fdos_error

fdos_ED_DRIVE:
;-----------
	mov	ax,ED_DRIVE		; bad drive
	jmps	fdos_error

fdos_ED_PATH:
	mov	ax,ED_PATH		; invalid path or drive
	jmps	fdos_error

fdos_ED_PROTECT:
	mov	ax,ED_PROTECT		; write protect error
	jmps	fdos_error

	Public	fdos_ED_ACCESS

fdos_ED_ACCESS:
	mov	ax,ED_ACCESS		; access denied error
	jmps	fdos_error

eject
;	MAKE DIRECTORY (MKDIR)

;	+----+----+----+----+----+----+
;	|    39   |        name       |
;	+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name

;	exit:
;	-----
;	AX:	0000 or error code ( < 0)

fdos_mkdir:
;----------
	call	redir_asciiz_offer
	call	local_disk		; get MXdisk, switch stack
	call	path_prep_chk		; parse path, walk down the tree
	call	finddfcbf		; find matching FCB
	 jnz	mkdir_access		; file/dir exists, "access denied"
	call	allocdir		; allocate a directory entry

	push	ax			; save pointer to directory entry
	lea	si,info_fcb+1		; SI -> file name
	xchg	ax,di			; DI -> DNAME
	mov	cx,11
	rep	movsb			; copy file name into directory buffer
;	lea	di,DATTS[si]
	mov	al,DA_DIR
	stosb				; mark it as a directory
	xor	ax,ax
	mov	cx,(32-12)
	rep	stosb			; zero remainder of new FCB
	pop	si			; SI -> directory

	call	stamp_dir_entry		; set time and date in FCB
	xor	ax,ax
	call	alloc_cluster		; Allocate a block
	 jc	mkdir_err		; Report Error(no room on disk)
	mov	si,dirp
	mov	DBLOCK1[si],ax		; Initialize 1st block of fcb
	push	ax			; save block
	call	update_fat		; Write the fat to disk
	call	update_dir		; Write the directory to disk
	pop	ax			; now initialise the sub-directory
	call	mkdir_init		;  '.' and '..' entries
	call	update_dir		; update directory entry
	mov	ax,5			; return a magic value for some
	mov	fdos_ret,ax		;  PD expansion program
	ret

mkdir_err:
;---------
	mov	bx,dirp			; can't make dir, no data space
	mov	DNAME[bx],0E5h		; so release our directory entry
	call	update_dir		; release this entry
mkdir_access:
	jmp	fdos_ED_ACCESS		; return "access denied" error

chk_no_dot:				; reject "." and ".." names
	cmp	info_fcb+1,'.'
	 je	mkdir_access
	ret

eject
;	REMOVE DIRECTORY (RMDIR)

;	+----+----+----+----+----+----+
;	|    3A   |        name       |
;	+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name

;	exit:
;	-----
;	AX:	0000 or error code ( < 0)

fdos_rmdir:
;----------
	call	redir_asciiz_offer
	call	local_disk		; get MXdisk, switch stack
	call	path_prep_chk		; parse path to bottom level
	call	finddfcbf		; try to locate directory entry
	 jz	fdos_rmdir20		; skip if we can't find it
	test	DATTS[bx],DA_DIR	; check directory attribute
	 jz	fdos_rmdir30		; O.K. if a directory
	call	chkcds			; make sure no-where current
	 jnc    fdos_rmdir40
	call	path_prep		; redo as rmdir_ok may destroy info
	call	finddfcbf		; find the entry again
	call	rmdir_ok		; make sure not in use
	call	finddfcbf		; find the entry again
if PASSWORD
    call    check_pwd_any      
endif
kill_entry:
	call	kill_file		; actually delete entry
update_dir_fat:
	call	update_dir		; update directory entry
	jmp	update_fat		; update file allocation table

fdos_rmdir20:
	jmp	fdos_ED_PATH		; "invalid path"

fdos_rmdir30:
	jmp	fdos_ED_ACCESS		; "access denied"

fdos_rmdir40:
	mov	ax,ED_DIR
	jmp	fdos_error

eject
;	CHANGE DIRECTORY (CHDIR)

;	+----+----+----+----+----+----+
;	|    3B   |        name       |
;	+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name

;	exit:
;	-----
;	AX:	0000 or error code ( < 0)


chdir_ED_PATH:
	jmp	fdos_ED_PATH		; all logical errors give "no path"

chdir_ED_DRIVE:
	mov	ax,ED_DRIVE		; bad drive error message
chdir_err:
	jmp	fdos_error

fdos_chdir:
;----------
	call	redir_asciiz_offer
	call	local_disk		; get MXdisk, switch stack
fdos_move_chdir:
;---------------
	mov	orig_drive,0FFFFh	; set drive to invalid
	les	di,dword ptr fdos_pb+2	; get string address
	call	get_path_drive		; from asciiz or default
	 jc	chdir_ED_DRIVE		;  continue if drive A: - Z:
	call	islocal			; reject networked drives
	 jc	chdir_ED_DRIVE		;  as bad drives
	call	path_prep_cont		; continue if drive legal
	 jc	chdir_err		; return error to application
	call	chk_no_dev		; no devices allowed
	call	chk_no_wild		; no wild cards allowed here
	cmp	orig_drive,0FFFFh	; check if assign to specified drive
	 jne	chdir10			; skip drive specified
	mov	ax,path_drive		; else same as path drive
	mov	orig_drive,ax
chdir10:
if JOIN
	call	offer_join		; are we opening a JOIN'd drive ?
	 jnc	chdir30			;  if so move to it and skip open_dir
endif
	call	chk_for_root		; check if we are in the root
	 je	chdir30			; if so, just skip the open_dir
	call	finddfcbf		; try to locate directory entry
	 jz	chdir_ED_PATH		; skip if no valid directory
	test	DATTS[bx],DA_DIR	; check directory attribute
	 jz	chdir_ED_PATH		; return if not a directory
if PASSWORD
	call	check_pwd_any		; check if PW prot'd & wrong PW
endif
	call	open_dir		; else open last directory
	 jc	chdir_ED_PATH		; return an error if we can't
chdir30:
	mov	ax,orig_drive		; get logical drive
	call	get_ldt			; ES:BX -> LDT_
	 jc	chdir10			; no LDT_, must be init time...
	mov	si,es:LDT_FLAGS[bx]	; normally we inherit parents
	mov	cx,es:LDT_ROOTLEN[bx]	; root block and drive
	mov	dx,es:word ptr LDT_NAME[bx]
	mov	bx,es:LDT_ROOT[bx] 	; if it's a "SUBST I: C:\PATH" form
	cmp	ax,path_drive		;  we pick up new root and drive
	 je	chdir50			;  like this
	mov	ax,path_drive		; ASCII drive from path drive
	mov	dx,'A'+256*':'		; make it into ASCII "C:"
	add	dx,ax			; in case LDT does not exist
	call	get_ldt			; get LDT_ in case it's subst'd
	 jc	chdir40
	mov	dx,es:word ptr LDT_NAME[bx]
chdir40:
	mov	bx,fdos_hds_blk		; root block from fdos_hds
chdir50:
	mov	di,offset pri_pathname	; build new LDT_ here
	or	si,LFLG_PHYSICAL	; drive is always physical
	mov	LDT_FLAGS[di],si
	mov	LDT_ROOTLEN[di],cx	; inherit rootlen from parent
	mov	LDT_ROOT[di],bx		; root as above
	mov	ax,fdos_hds_blk		; current block from HDS
	mov	LDT_BLK[di],ax
	mov	al,fdos_hds_drv		; current drive we from HDS
	mov	LDT_DRV[di],al
	mov	ax,word ptr current_ddsc
	mov	word ptr LDT_PDT[di],ax
	mov	ax,word ptr current_ddsc+WORD
	mov	word ptr LDT_PDT+WORD[di],ax
	push ss ! pop es		; now build ASCII path
;	lea	di,LDT_NAME[di]		; ES:DI -> name
	xchg	ax,dx
	stosw				; plant 'A:'
	mov	ax,'\'
	stosw				; make that 'A:\',0
	dec	di			; point at NUL
	mov	fdos_hds_root,0		; we want to go back to root
	mov	fdos_pb+6,di		; rebuild into LDT_ at the address
	mov	fdos_pb+8,es
	push ds ! pop es
chdir60:
	call	hdsblk			; get current block
if JOIN
	 jnz	chdir65			; if not at root unparse
	call	check_join		; check if drive is joined to another
	 jz	chdir80			; at root, unJOIN'd, so exit
					; we are at the root of this drive
	xchg	al,fdos_hds_drv		; update HDS drive so we can force
	push	es			;  an exit next time round
	call	get_ldt			; now copy the JOIN'd name
	push ds ! push es ! pop ds ! pop es	
	lea	si,LDT_NAME+3[bx]	; point to my JOIN data
	mov	di,offset save_area
	call	copy_asciiz
	push es ! pop ds
	pop	es
	jmps	chdir70			; copy this into place
chdir65:
else
	 jz	chdir80			; if at root, we're done
endif
	call	parent2save_area	; parental name written in save_area
chdir70:
	dec	di			; DI -> '\0' at end
	mov	al,'\' ! stosb		; add a trailing backslash
	sub	di,offset save_area+1
	mov	dx,di			; DX = length of name

	push	es			; insert name into user buffer
	les	di,dword ptr fdos_pb+6
	mov	al,0 ! scasb		; check if first directory
	 je	chdir75			; if not 1st in path
	inc	dx			; we need space for '\'
chdir75:
	dec	di			; adjust for scan
	call	mkspace_parent		; move ES:DI up by DX bytes
	mov	di,fdos_pb+6		; ES:DI -> user buffer
	rep	movsb			; copy directory into user buffer
	pop	es			; ES = local segment again
	jmp	chdir60			; try again til root

chdir80:				; arrive here when root reached
	mov	ax,orig_drive		; get logical drive
	call	get_ldt			; ES:BX -> LDT_
	 jc	chdir85			; no LDT_, what happended ?
	push	ds
	mov	di,bx			; ES:DI -> destination LDT_
	push ss ! pop ds
	mov	si,offset pri_pathname	; DS:SI -> new LDT_ we just built
	mov	cx,LDT_LEN
	rep	movsb			; copy the new LDT_ into place
	pop	ds
	cmp	ax,path_drive		; if the drive's are same
	 je	chdir85			;  "CD" or "SUBST d:=d:path"
	or	es:LDT_FLAGS[bx],LFLG_SUBST
	lea	di,LDT_NAME[bx]		; ES:DI -> name
	xor	ax,ax
	mov	cx,LDT_FLAGS-LDT_NAME	; name can be this long
	repne	scasb			; look for terminating NUL
	sub	di,bx			; get # chars skipped
	dec	di			; forget the NUL
	cmp	di,3			; root is special case again !
	 jbe	chdir85
	mov	es:LDT_ROOTLEN[bx],di	; set new root position
chdir85:
	ret

eject
;	CREATE FILE (CREAT)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    3C   |        name       |  mode   |  attrib |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name
;	mode:	open mode for handle
;   attrib: attribute for file

;	exit:
;	-----
;	AX:	file handle or error code ( < 0)

fdos_creat:
;----------
	call	redir_asciiz_dev_offer	; offer it as a network device
	call	asciiz_dev_offer	; offer it as a local device
	call	redir_asciiz_file_offer	; offer it as a network file
	call	local_disk		; get MXdisk, switch stack
	call	mustbe_free_handle	; make sure we have spare handle
	call	path_prep		; parse path, walk down the tree
	call	check_device		; is it a device ?
	 jc	creat_disk
	jmp	open_dev		; open as a device handle
creat_disk:
	call	chk_no_dot_or_wild
	test	byte ptr fdos_pb+8,DA_VOLUME
	 jz	creat_nolbl		; skip if not volume label
	call	mustbe_nolbl		; do we have an existing label ?
	jmps	creat_new_file		; no, create one
creat_nolbl:
	call	finddfcbf		; find entry, ignore labels
	 jz	creat_new_file		; skip if it doesn't exist yet

	call	check_no_dir		; make sure not a directory
	call	check_ro		; check if file is read/only
	call	close_if_open		; make sure not open by any process
if PASSWORD
	call	check_pwd_d		; check if PW req'd & supplied	
endif
	mov	bx,dirp
	mov	ax,DBLOCK1[bx]		; release all blocks of the file
	call	delfat			; so it is truncated to zero size
	call	update_fat		; update file allocation table
	jmps	creat2			; reinitialize directory entry

creat_new_file:				; create new file - shared by MKNEW
	call	allocdir		; allocate a directory entry
creat2:
	mov	bx,dirp			; BX -> directory entry
	lea	di,DNAME[bx]		; DI -> name offset in dir buffer
	mov	si,offset info_fcb+1	; SI -> name offset in FCB
	mov	cx,11
	rep	movsb			; copy name into directory
	mov	ax,fdos_pb+8		; get creation file attribute
	mov	dx,fdos_pb+6		; open in r/w, compatibilty mode
	test	al,not (DA_RO+DA_SYSTEM+DA_HIDDEN+DA_ARCHIVE)
	 jz	creat5			; allow r/o, hidden, system, archive
	test	al,DA_VOLUME		; failure as file, creating a label?
	 jz	creat_access_err	;  no, then it's really a problem
	mov	dl,DHM_RO+DHM_LOCAL	; open in compatibility mode
creat5:
	or	al,DA_ARCHIVE		; always create as new file
	stosb				; update directory attributes
	sub	ax,ax			; zero out remainder of entry
	mov	cx,32-12
	rep	stosb			; zero remainder of entry

	push	bx			; save dirp
	push	dx			; save the open mode
	call	stamp_dir_entry		; set time and date in FCB
	call	update_dir		; update directory
	pop	ax			; restore open mode
	pop	bx			; restore dirp
	jmp	creat_handle		; now allocate file handle

creat_access_err:
	jmp	fdos_ED_ACCESS		; return "access denied"


eject
;	OPEN FILE (OPEN)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    3D   |        name       |  mode   |  attrib |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name
;   mode:   open mode 
;	attrib:	file attrib for search (default = 16h)

;	exit:
;	-----
;	AX:	file handle or error code ( < 0)

fdos_open:
;---------
	call	redir_asciiz_dev_offer	; offer it as a network device
	call	asciiz_dev_offer	; offer it as a local device
	call	redir_asciiz_file_offer	; offer it as a network file
	call	local_disk		; get MXdisk, switch stack
	call	mustbe_free_handle	; make sure we have spare handle
	call	path_prep		; parse the path, go to bottom level
	call	check_device
	 jc	open_disk
	jmp	open_dev		; open as a device
open_disk:
	call	chk_no_wild		; we don't allow no wild cards here
	call	finddfcbf		; search by name
	 jz	open8			; bail out if not found
	mov	al,byte ptr fdos_pb+8	; check requested attributes
if PASSWORD
	cmp	word ptr DPWD[bx],0
	 je	open1
	or	al,DA_HIDDEN
open1:
endif
	not	al
	and	al,DA_HIDDEN+DA_SYSTEM
	or	al,DA_DIR+DA_VOLUME
	test	DATTS[bx],al
	 jnz	open9			; error if attrib not as requested
	mov	ax,fdos_pb+6		; determine open mode
if PASSWORD
	sub	dx,dx			; assume no PW conflicts
endif
	test	al,DHM_RW+DHM_WO	; test if read/only access
	 jz	open2
if PASSWORD				; if write or r/w check writes
	or	dx,PWM_W		;  check for write password
endif
;	mov	bx,dirp			; get directory pointer
	test	DATTS[bx],DA_RO		; read/only file?
	 jz	open2			;  no, skip FCB check
	test	ax,DHM_FCB		; if FCB open of read-only file
	 jz	open9			; allow it, but adjust to read-only
	and	al,not DHM_RWMSK	;  open mode and proceed
open2:
if PASSWORD
	test	al,DHM_WO		; will we try to read?
	 jnz	open3			; skip if not open for reading
	or	dx,PWM_R + PWM_E	; else check for read/exec password
open3:
	push	ax			; save the open mode
	xchg	ax,dx			; AX = password modes to check for
	call	check_pwd		; check for r/w passwords
	pop	ax
endif
	jmp	open_handle		; now allocate file handle

open8:
	jmp	fdos_ED_FILE		; "file not found"
open9:
	jmp	fdos_ED_ACCESS		; return "access denied" error


eject
;	CLOSE FILE (CLOSE)

;	+----+----+----+----+
;	|    3E   |  handle |
;	+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle to be closed

;	exit:
;	-----
;	AX:	3Exx (where xx = # time it was open) or error code ( < 0)

fdos_close:
;----------
	call	vfy_dhndl_ptr		; check file handle #
	call	redir_dhndl_offer
	call	local_disk		; get MXdisk, switch stack
	call	check_handle		; check if legal file handle
	mov	ax,es:DHNDL_COUNT[bx]	; return # times file WAS open
	mov	ah,3Eh			; AH = MS_X_CLOSE
    mov fdos_ret,ax     ; return 3Exx if successful
	 jc	close20			; skip if character device close
close_dhndl:
	call	release_handle		; release file handle into pool
	callf	ss:share_stub+S_CLOSE	; call the fdos stub routine
	dec	es:DHNDL_COUNT[bx]	; one less XFN refers to this IFN
	test	es:DHNDL_WATTR[bx],DHAT_REMOTE+DHAT_CLEAN
	 jnz	close10			; skip update if not dirty local file
	call	select_handle		; select the disk
	call	file_update		; update directory & FAT if written
close10:
	ret

close20:				; handle refers to character device
	jmp	close_dev		; close the device handle

eject
;	READ FROM FILE (READ)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    3F   |  handle |       buffer      |  count  |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle
;	buffer:	buffer to read into
;	count:	max. number of bytes to read

;	exit:
;	-----
;	AX:	byte count or error code ( < 0)

fdos_read:
;---------
	call	vfy_dhndl_ptr		; check file handle #
	test	es:DHNDL_MODE[bx],DHM_WO
	 jnz	rw_ED_ACCESS		; fail if open write only
	call	redir_dhndl_offer
	test	es:DHNDL_WATTR[bx],DHAT_DEV
	 jnz	fdos_read_dev		; skip if character device
	call	local_disk		; get MXdisk, switch stack
	call	verify_handle		; check if legal file handle
	mov	cx,fdos_pb+8		; get requested transfer length
	mov	ax,es:DHNDL_POSLO[bx]	; If we get an error we want to have
	mov	dx,es:DHNDL_POSHI[bx]	; xfer len adjusted for EOF (CopyIIpc)
	sub	ax,es:DHNDL_SIZELO[bx]	; are we beyond EOF ?
	sbb	dx,es:DHNDL_SIZEHI[bx]
;	 jb	read2			; beyond EOF already, no action required
;	 ja	read2			; more the 64K to go, no problems
	 jnz	read2
	cmp	ax,cx			; do we want more than there is?
	 jae	read2			; yes, shorten the fdos_pb count
	sub	fdos_pb+8,ax		;  in case of a critical error
read2:
	mov	fdrwflg,1		; we're reading
	les	di,dword ptr fdos_pb+4	; get disk transfer address
	call	fdosrw			; read from file
	xor	ax,ax			; return OK
	xchg	ax,fdos_ret		; get returned byte count
	mov	fdos_pb+8,ax		; return in requested count
	ret

fdos_read_dev:
	jmp	read_dev		; read from character device

rw_ED_ACCESS:
	mov	bx,ED_ACCESS		; return access denied
	ret

eject
;	WRITE TO FILE (WRITE)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    40   |  handle |       buffer      |  count  |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle
;	buffer:	buffer to be wriiten
;	count:	max. number of bytes to write

;	exit:
;	-----
;	AX:	byte count or error code ( < 0)

fdos_write:
;----------
	call	vfy_dhndl_ptr		; check file handle #
	test	es:DHNDL_MODE[bx],DHM_WO+DHM_RW
	 jz	rw_ED_ACCESS		; fail if not open in r/w or w mode?
	call	redir_dhndl_offer
	test	es:DHNDL_WATTR[bx],DHAT_DEV
	 jnz	fdos_write_dev		; skip if not character device
	call	local_disk		; get MXdisk, switch stack
	call	verify_handle		; check if legal file handle
	mov	fdrwflg,0		; we're writing
	les	di,dword ptr fdos_pb+4	; get disk transfer address
	mov	cx,fdos_pb+8		; get requested transfer length
	call	fdosrw			; write to file file

	xor	ax,ax			; return OK
	xchg	ax,fdos_ret		; get returned byte count
	mov	fdos_pb+8,ax		; return in requested count
	ret

fdos_write_dev:
	jmp	write_dev		; write to character device
	
eject
;	DELETE FILE (UNLINK)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    41   |        name       |  *****  |  attrib |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name
;	attrib:	delete mask (FCB only..)

;	exit:
;	-----
;	AX:	0000 or error code ( < 0)


fdos_unlink:
;-----------
	call	redir_asciiz_offer
	call	local_disk		; get MXdisk, switch stack
	call	path_prep		; parse the path, go to bottom level
	call	chk_no_dev		; can't be a device
					; look to see is the call is a remote
	cmp	ss:remote_call,0	;  a 21/5D00 one or a local FCB one
	 jz	unlink1			; neither, no wild cards allowed
	 jns	unlink2			; if it's an FCB call we need to check
	test	fdos_pb+8,DA_VOLUME	;  for deleting a VOLUME label
	 jz	unlink2
	call	find_labelf		; lets find the volume label
	 jz	unlink4			;  reporting error if we don't match
	jmp	kill_entry		; found it - delete it

unlink4:
	jmp	fdos_ED_FILE		; return "file not found"

unlink1:
	call	chk_no_wild		; make sure not a wild card
unlink2:
	call	setenddir		; search from the start

; We now skip any labels/dirs, returning ED_FILE if no other matches
unlink3:
	call	finddfcb		; try to locate directory entry
	 jz	unlink4			;  returning an error on failure
	test	DATTS[bx],DA_DIR+DA_VOLUME
	 jnz	unlink3			; make sure it isn't a label/directory

unlink5:
; we have a match, so return ED_ACCESS if we can't find a file we can delete
	call	unlink_attribs		; AL = attribs we can't delete
	 jz	unlink7			; yes, go for it
	call	finddfcb		; look for another entry
	 jnz	unlink5			;  try this one instead
	jmp	fdos_ED_ACCESS		; "access denied" if nothing deleted
					;  since we found at least 1 file

; we will delete at least one file, so now we succeed
unlink6:
	call	unlink_attribs		; AL = attribs we can't delete
	 jnz	unlink8			; yes, go for it
unlink7:
	call	close_if_same_psp	; make sure not open by any process
if PASSWORD
	call	check_pwd_d		; check for password
endif
	call	kill_file		; delete file
unlink8:
	call	finddfcb		; look for another entry
	 jnz	unlink6			;  try and delete it if we find one
	jmp	update_dir_fat		; write out dirty directories/FAT


unlink_attribs:
; On Entry:
;	BX -> directory entry
; On Exit:
;	AL = attribute mask which prevents deletion
;	ZF = 1 if delete OK, ZF = 0 if file should be skipped
;
	mov	al,byte ptr fdos_pb+8	; check attributes
if PASSWORD
	cmp	word ptr DPWD[bx],0
	 je	unlink_attribs10
	or	al,DA_HIDDEN
unlink_attribs10:
endif
	not	al
	and	al,DA_HIDDEN+DA_SYSTEM+DA_RO
    or  al,DA_DIR+DA_VOLUME 
	test	DATTS[bx],al		; should we delete this entry ?
	ret


eject
;	GET/SET FILE POSITION (LSEEK)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    42   |  handle |       offset      !  method |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle
;	offset:	long integer offset
;	method:	0 = begin, 1 = current, 2 = end of file

;	exit:
;	-----
;	AX:	0000 or error code ( < 0)
;	offset:	new offset

fdos_lseek:
;----------
	call	vfy_dhndl_ptr		; check file handle #
	call	redir_dhndl_offer
	test	es:DHNDL_ATTR[bx],DHAT_DEV
	 jnz	lseek_dev		; skip if character device
	call	local_disk		; get MXdisk, switch stack
	call	check_handle		; check if legal file handle
	mov	ax,fdos_pb+4		; get 32-bit file offset
	mov	dx,fdos_pb+6		; into AX,DX
	mov	cx,fdos_pb+8		; get seek mode
	 jcxz	lseek4			; seek from beginning
	dec	cx
	 jz	lseek2			; seek from current position
	dec	cx
	 jz	lseek3			; seek from end
	mov	ax,ED_DATA		; else invalid seek mode
	jmp	fdos_error		; return error code
lseek1:
	ret				; return error code

lseek2:					; seek mode 1: relative to position
	add	ax,es:DHNDL_POSLO[bx]
	adc	dx,es:DHNDL_POSHI[bx]
	jmps	lseek4			; update new position

lseek3:					; seek mode 2: relative to end
	add	ax,es:DHNDL_SIZELO[bx]
	adc	dx,es:DHNDL_SIZEHI[bx]	; add file size + offset
lseek4:					; seek mode 0: set absolute position
	mov	es:DHNDL_POSLO[bx],ax
	mov	es:DHNDL_POSHI[bx],dx
	mov	fdos_pb+4,ax		; set return values
	mov	fdos_pb+6,dx
	ret

lseek_dev:				; jump here if character device
	mov	si,2[bp]		; SI -> parameter block
	xor	bx,bx
	mov	4[si],bx		; always return result of 0000h
	mov	6[si],bx
	ret

eject
;	GET/SET FILE ATTRIBUTES (CHMOD)

;	+----+----+----+----+----+----+----+----+----+----+
;	|    43   |        name       |   flag  | attrib  |
;	+----+----+----+----+----+----+----+----+----+----+
;	|        size       |
;	+----+----+----+----+

;	entry:
;	------
;	name:	pointer to ASCIIZ file name
;	flag:	00 = get attrib/size
;		01 = set attrib
;	attrib:	file attribute if flag=1
;
;	exit:
;	-----
;	AX:	0000 or error code ( < 0)
;	attrib:	file attribute if flag=0
;	size:	file size if flag=0
;
if PASSWORD
;	entry:
;	------
;	name:	pointer to ASCIIZ file name
;	flag:	02 = get password mode
;		03 = set pw mode/password
;		04 = get encrypted password
;		05 = set encrypted password
;	attrib:	password mode if flag = 3,5
;	dma:	ascii     password if flag = 3
;		encrypted password if flag = 5
;	
;	exit:
;	-----
;	AX:	0000 or error code ( < 0)
;	attrib:	file's attribute if flag = 0
;		password mode if flag = 2
;		encrypted password if flag = 4
endif
if UNDELETE
;	entry:
;	------
;	name:	pointer to ASCIIZ file name
;	flag:	80 = undelete file
;		81 = purge file
;	dma:	result of sucessful search
;
;	exit:
;	-----
;	AX:	0000 or error code ( < 0)
;
endif

fdos_chmod:
;----------
if UNDELETE
	mov	si,2[bp]		; SI -> parameter block
	mov	al,6[si]		; AX = flag
	sub	al,80h			; is it undelete or purge
	 jb	fdos_chmod_path		; if so set
	cmp	al,1
	 ja	fdos_chmod_path
	call	local_disk		; get MXdisk, switch stack
	call	select_from_DTA		; prepare for the search
	mov	chdblk,0		; don't assume sequential access
	dec	dcnt			; retract so we find the same entry
	call	find_pending_delete	; did we find it ?
	 jz	chmod_notfound		; No, then skip
	call	hdsblk			; AX = directory root cluster
	xchg	ax,dx			; DX = dir cluster
	mov	cx,dcnt			; CX = directory index for entry
	mov	ax,fdos_pb+6		; get operation type
	mov	ah,DELW_UNDEL		; assume we are about to undelete
	cmp	al,80h			; is it undelete ?
	 je	fdos_undelete_purge
	mov	ah,DELW_PURGE		; no, it must be purge entry
fdos_undelete_purge:
	mov	al,physical_drv		; give delwatch a chance to do it
	callf	ss:fdos_stub
	 jc	fdos_ED_FUNCTION	; return error if DELWATCH not there
	mov	fdos_ret,ax		;  else return result
	ret
fdos_chmod_path:
endif
	call	redir_asciiz_offer
	call	local_disk		; get MXdisk, switch stack

	call	path_prep		; parse the path, go to bottom level
	call	chk_no_wild		; can't have wildcards
	call	finddfcbf		; find first matching FCB
	 jnz	chmod10			; if we can't find a file/dir
	call	chk_for_root		;  check if we are in the root
	 jnz	chmod_notfound		; if so return directory attribute
	mov	cx,fdos_pb+6		;  but only for get attributes
	 jcxz	chmod_root		;  other fall through to not found
chmod_notfound:
	jmp	fdos_ED_FILE
chmod_root:
	mov	fdos_pb+8,DA_DIR	; return directory attribute
	ret

fdos_ED_FUNCTION:
	mov	ax,ED_FUNCTION		; invalid subfunction
	jmp	fdos_error

chmod10:
	mov	bx,dirp			; BX -> matching directory entry
	mov	ax,DSIZE[bx]
	mov	fdos_pb+10,ax
	mov	ax,DSIZE+WORD[bx]
	mov	fdos_pb+12,ax
	xor	ax,ax
	mov	al,DATTS[bx]		; get directory attributes
	mov	cx,fdos_pb+6		; get function #
	 jcxz	chmod30			; always allow get attribs
if PASSWORD
	cmp	cl,5			; validate sub-function number
	 ja	chmod15
	mov	ax,DPWM[bx]		; assume return password mode
	and	ax,PWM_ANY		; isolate password mode bits
	cmp	cl,2			; is it get mode ?
	 je	chmod30			; yes, just return it
	push	ax
	push	cx
	call	check_pwd_any		; check the password
	pop	cx
	pop	ax
	mov	dx,fdos_pb+8		; DX = new attributes
	cmp	cl,2			; is it get mode ?
	 ja	chmod20			; how about other password functions?
else
	cmp	cl,1			; validate sub-function number
	 ja	chmod15
	mov	dx,fdos_pb+8		; DX = new attributes
endif
; Set new file attrib
; BX = DIRP
; DX = attrib
;
	test	dl,DA_DIR		; directory bit not allowed
	 jnz	chmod12
	xor	dl,DATTS[bx]		; check which bits are changed
	and	dl,not DA_DIR		; don't check or flip directory bit
	test	dl,DA_FIXED		; trying to change the unchangeable?
	 jz	chmod13
chmod12:
	jmp	fdos_ED_ACCESS		; return "access denied"
chmod13:
	xor	DATTS[bx],dl		; set new attributes
if PASSWORD
	jmps	chmod90
else
	jmp	update_dir		; update directory
endif

chmod15:
	mov	ah,PASSWD_CHMOD		; call out to SECURITY TSR
	callf	ss:fdos_stub
	 jc	fdos_ED_FUNCTION
	jmps	chmod90

if PASSWORD
chmod20:
; Password support for functions 3-5
; BX = DIRP
; CX = flag
; DX = attrib
;
	push	bx
	push	cx
	push	dx
	mov	di,S_DENY_IF_OPEN	; check if file already open
	call	check_with_share	; and stop if it is
	pop	dx
	pop	cx
	pop	bx
	mov	ax,DPWD[bx]		; assume get encrypted password
	cmp	cl,4			;  was it ?
	 jne	chmod50
endif					; yes, return encrypted password
chmod30:
	mov	fdos_pb+8,ax
	ret


if PASSWORD
; Password support for functions 3/5
; BX = DIRP
; CX = flag
; DX = attrib
; 
chmod50:				; set password/password mode
	test	dh,80h			; assign new password?
	 jz	chmod70			; skip if mode change only
	mov	di,offset save_area	; ES:DI -> local structure
	push	ds
	call	lds_si_dmaptr		; DS:SI -> users DMA address
	lodsw				; AX = possible encrypted password
	cmp	cl,5			; was it set encrypted password ?
	 je	chmod60
	dec	si			; no, the DMA buffer contains
	dec	si			;  an 8 character password so we
	call	hash_pwd		;  compute password hash code
chmod60:
	pop	ds
	or	DATTS[bx],DA_HIDDEN	; file will be hidden
	mov	DPWD[bx],ax		; set new file password
	test	ax,ax			; null password?
	 jnz	chmod70
	xor	dx,dx			; can't be protected....
chmod70:
	mov	ax,not PWM_ANY		; clear existing file
	and	DPWM[bx],ax		;  password mode bits
	not	ax			; isolate password mode bits
	and	dx,ax			;  in new password mode
	 jz	chmod80			; if no protection, leave them off
	test	DATTS[bx],DA_DIR	; directories protected in all modes
	 jz	chmod80			; skip if not a directory
	xchg	ax,dx			; force all modes on
chmod80:
	or	DPWM[bx],dx		; set password mode bits
	test	dx,dx			; test if protection enabled
	 jnz	chmod90			; skip if any protection still active
	mov	DPWD[bx],dx		; remove the password and hidden bit
	and	DATTS[bx],not DA_HIDDEN	;  as file is no longer protected
endif
chmod90:
	jmp	update_dir		; now update the directory






eject
;	DUPLICATE FILE HANDLE (DUP)

;	+----+----+----+----+----+----+
;	|    45   |  handle |  newhnd |
;	+----+----+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle

;	exit:
;	-----
;	newhnd: new file handle
;	AX:	duplicate file handle or error code ( < 0)

fdos_dup:
;--------
	call	find_xfn		; find new external file #
	mov	si,2[bp]		; SI -> parameter block
	mov	ds:4[si],di		; save new handle #
	 jnc	fdos_fdup		; share the code with DUP2
	mov	bx,ED_HANDLE		; can't find a handle
	ret
eject
;	FORCE DUPLICATE FILE HANDLE (DUP2)

;	+----+----+----+----+----+----+
;	|    46   |  handle |  newhnd |
;	+----+----+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle
;	newhnd: new file handle

;	exit:
;	-----
;	AX:	duplicate file handle or error code ( < 0)

fdos_fdup:
;---------
	call	vfy_dhndl_ptr		; check file handle #
	call	local_disk		; get critical section locks
	call	get_xftptr		; ES:DI -> XFN table
	 jc	dup_err			; we need one..
	mov	bx,fdos_pb+4		; get user file number (0-19)
	cmp	bx,cx			; is it sensible ?
	 jae	dup_err
	cmp	es:byte ptr [di+bx],0FFh
	 jne	dup_err			; handle should be closed by PCMODE..
	call	check_handle		; check if legal file handle
	inc	es:DHNDL_COUNT[bx]	; another user
	call	dup_dev			; inform device driver it's happened
	call	get_xftptr		; ES:DI -> XFT's
	mov	bx,fdos_pb+2		; get XFN to dup from
	mov	al,es:[di+bx]		; get it's IFN
	mov	bx,fdos_pb+4		; BX = XFN to dup to
	mov	es:[di+bx],al		; it gets same IFN
	mov	fdos_ret,bx		; return XFN to caller
	ret

dup_err:				; complain someone stole my handle
	jmp	fdos_ED_H_MATCH

eject
;	GET CURRENT DIRECTORY
;	
;	+----+----+----+----+----+----+----+----+
;	|    47   |   drive |        path       |
;	+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	drive:	drive to get path for
;	path:	address of 64 byte path buffer to be
;		filled in with current path

;	exit:
;	-----
;	BX:	0100 or error code

fdos_curdir:
;-----------
	call	local_disk		; it's a disk function
	call	get_pb2_drive		; get specified drive in AL
	call	get_ldt			; ES:BX -> LDT_ for drive
	 jc	fdos_curdir30
	mov	dx,es:LDT_FLAGS[bx]
	test	dx,LFLG_NETWRKD
	 jnz	fdos_curdir10
	test	dx,LFLG_PHYSICAL
	 jz	fdos_curdir30
	call	select_unique		; select the drive for media changes
	les	bx,ss:current_ldt	; ES:BX -> LDT_ for this drive
	cmp	es:LDT_BLK[bx],0FFFFh	; is LDT valid
	 jne	fdos_curdir10
	call	rebuild_ldt_curdir	; no, better rebuild it
fdos_curdir10:
	push	ds
	push es ! push bx		; save LDT
	les	di,dword ptr fdos_pb+4	; ES:DI -> destination buffer
	pop si ! pop ds			; DS:SI -> LDT
	add	si,ds:LDT_ROOTLEN[si]	; skip the '\\server\dir'
	lodsb				; eat the slash
	call	check_slash		; if it is one..
	 je	fdos_curdir20
	dec	si			; I didn't mean it!
fdos_curdir20:
	call	copy_asciiz		; copy the string
	pop	ds
	mov	fdos_ret,100h		; return 100h for success
    ret      

fdos_curdir30:
	jmp	fdos_ED_DRIVE		; naughty - it's a bad drive


eject
;	GET DISK PARAMETER BLOCK

;	+----+----+----+----+----+----+----+----+----+----+
;	|    48   |  drive  |        dpb        | adjust  |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	drive:	drive to get information about
;		(top bit of word set if free space not required)

;	exit:
;	-----
;	AX:	0000 or error code ( < 0)
;	dpb:	address of DOS DPB (offset/segment)
;	adjust:	delwatch adjustment of free space

fdos_getdpb:
;-----------
	call	redir_drv_offer
	call	local_disk		; get MXdisk, switch stack
	call	get_pb2_drive		; get drive from parameter block
	call	logical2physical	; AX = physical drive
	call	select_physical_drv
	test	fdos_pb+2,8000h		; free space required ?
	 jnz	fdos_getdpb10
	call	update_ddsc_free	; then make sure it is up to date
if DELWATCH
	xor	cx,cx			; assume no adjustment
	mov	ah,DELW_SPACE
	mov	al,physical_drv		; now we call DELWATCH to
	callf	fdos_stub		;  add pending deletes
	mov	fdos_pb+8,cx		; return free space adjust value
endif
fdos_getdpb10:
	les	ax,ss:current_ddsc
	mov	fdos_pb+4,ax
	mov	fdos_pb+6,es
	ret

eject
;	FLUSH BUFFERS

;	+----+----+
;	|    49   |
;	+----+----+

;	entry:
;	------
;	none

;	exit:
;	-----
;	none

fdos_flush:
;----------
	call	local_disk		; be alone...
fdos_flush_local:
; Entry point for people who already have the MX disk
	xor	dx,dx			; starting with drive A
fdos_flush10:
	push	dx
	call	mark_ldt_unsure		; ask for LDT's to be relogged
	xchg	ax,dx			; AL = drive to check
	mov	ah,BF_DIRTY
	call	buffers_check		; any buffers on this drive?
	 jz	fdos_flush20		; skip flush if none
	call	select_physical_drv	; select drive in AL
	call	update_fat		; flush all FAT buffers
;	call	update_dir		; dir always up to date
	call	update_dat		; flush all data buffers
fdos_flush20:
	pop	dx
	xchg	adrive,dl
	call	discard_all		; discard all buffers
	xchg	adrive,dl
	inc	dx			; onto next drive
	cmp	dl,phys_drv
	 jb	fdos_flush10
	mov	al,0FFh			; AL = all drives
	jmp	hshdscrd		; forget about our hashing
;	ret


eject
;	SELECT DRIVE

;	+----+----+----+----+
;	|    4A   |  drive  |
;	+----+----+----+----+

;	entry:
;	------
;	drive:	drive (zero based) to select as default drive

;	exit:
;	-----
;	current_dsk: drive (if legal)

fdos_select:
;-----------
	call	local_disk		; cheap way to get MX
	mov	ax,fdos_pb+WORD		; AL = drive
	call	get_ldt			; ES:BX -> LDT for drive A
	mov	dx,ax			; DL = logical drive
	 jc	fdos_select10		; no LDT, treat as physical=logical
	mov	ax,es:LDT_FLAGS[bx]	; get the LDT_FLAGS
	test	ax,LFLG_NETWRKD		; NETWORK drives are OK, but we must
	 jnz	fdos_select20		;  skip physical selection bit
	test	ax,LFLG_PHYSICAL
	 jz	fdos_select30		;  skip physical selection bit
if JOIN
	test	ax,LFLG_JOINED		; JOINed drives are bad news
	 jnz	fdos_select30		;  so don't select one
endif
	mov	al,es:LDT_NAME[bx]	; get ASCII drive letter
	call	toupper			; it was ascii
	sub	al,'A'			;  make it zero based
fdos_select10:
	call	get_ddsc		; ES:BX -> DDSC_ for drive
	 jc	fdos_select30		;  no, don't select
fdos_select20:
	mov	ss:current_dsk,dl	; new logical disk selected
fdos_select30:
	ret

eject
;	EXECUTE CHILD (EXEC)

;	+----+----+----+----+
;	|    4B   |  pspseg |
;	+----+----+----+----+

;	entry:
;	------
;	pspseg: segment of child PSP

;	exit:
;	-----
;	AX:	0000h

fdos_exec:
;---------
	call	local_disk		; get MXdisk, switch stack
	mov	cx,fdos_pb+2
	 jcxz	exec30			; no duplicate handles if no new PSP
	mov	di,offset PSP_XFT
	mov	es,cx			; get child PSP
	mov	cx,XFNMAX		; get # of handles in child PSP
	mov	PSP_XFNMAX,cx
	mov	PSP_XFTOFF,di
	mov	PSP_XFTSEG,es
	mov	al,0FFh			; assume all handles closed
	rep	stosb
	xor	si,si			; start with XFN 0
exec10:
	call	get_xftptr		; ES:DI -> XFN table
	 jc	exec30			; stop if none
	add	di,si
	mov	al,es:[di]		; get IFN of old handle
	call	ifn2dhndl		; ES:BX -> DHNDL_
	 jc	exec20
	mov	cx,es:DHNDL_COUNT[bx]	; skip files that aren't open
	 jcxz	exec20
	test	es:byte ptr DHNDL_WATTR+1[bx],DHAT_LOCAL/100h
	 jnz	exec20			; don't inherit private files
	test	es:byte ptr DHNDL_MODE+1[bx],DHM_FCB/100h
	 jnz	exec20			; don't inherit FCB's
	inc	cx
	mov	es:DHNDL_COUNT[bx],cx	; increment in-use count
	push	ax
	call	dup_dev			; inform device driver it's happened
	pop	ax
	mov	es,fdos_pb+2		; get child PSP
	mov	es:PSP_XFT[si],al	; inherit this IFN
exec20:
	inc	si			; next file handle
	cmp	si,XFNMAX
	 jb	exec10			; inherit all file handles
exec30:
	ret


eject
;	FIND FIRST FILE

;	+----+----+----+----+----+----+----+----+----+----+
;	|    4E   |        name       |  *****  |  attrib |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	name:	pointer to ASCIIZ file name
;	attrib:	attribute to be used in search
;		nb. API addition - attrib bit 7 set returns starting cluster
;	
;	exit:
;	-----
;	AX:	0000 or error code ( < 0)

;	Note:	This call returns matching files in
;		the current DMA address and also saves
;		the BDOS state in the there.
;

fdos_first:
;----------
	call	redir_asciiz_dev_offer	; offer it as a network device
	call	asciiz_dev_offer	; offer it as a local device
	call	redir_asciiz_file_offer	; offer it as a network file
	call	local_disk		; get MXdisk, switch stack
	call	path_prep		; parse path, walk down the tree
	call	check_device		; if it a device
	 jnc	fdos_first10
	mov	ax,fdos_pb+8		; get search attribute
	mov	attributes,al		; set for attribute match
	mov	dcnt,0FFFFh		; search from beginning
	jmps	search_next		; find next matching file

fdos_first10:
	jmp	first_dev		; return device name

next_deverr:				; NEXT after FIRST on device:
	mov	bx,ED_FILE		; "no more files"
	ret

eject
;	FIND NEXT FILE

;	+----+----+
;	|    4F   |
;	+----+----+

;	entry:
;	------
;	
;	exit:
;	-----
;	AX:	0000 or error code ( < 0)

;	Note:	This call returns matching files in
;		the current DMA address and also saves
;		the BDOS state in the there.
;

fdos_next:
;---------
	push	ds
	call	lds_si_dmaptr		; DS:SI -> users DMA address
	lodsw
	xchg	ax,dx			; DH = local drive
	pop	ds
	cmp	dh,0FFh			; check if FIRST was character device
	 je	next_deverr		; "no more files" if device
	call	redir_snext_offer
	call	local_disk		; get MXdisk, switch stack
	call	select_from_DTA		; prepare for the search
;	jmp	search_next		; now go and look for next file

search_next:
;-----------
	mov	chdblk,0		; don't assume sequential access
	mov	al,attributes		; are we looking for a VOL label?
	test	al,DA_DIR+DA_SYSTEM+DA_HIDDEN
	 jnz	search_n10		; these bits take precedence
	test	al,DA_VOLUME		; searching for label?
	 jz	search_n10		; search for directory label only
if UNDELETE
	cmp	al,DA_DELWATCH+DA_VOLUME
	 jne	search_vol		; searching for pending delete entries?
	call	find_pending_delete	; did we find it ?
	 jz	search_next_err		; No, then skip
	jmps	search_n30		; save search state for user
search_vol:
endif
	call	find_label		; did we find it ?
	 jnz	search_n30		; save search state for user
search_next_err:
	jmp	fdos_ED_ROOM		; else end of directory

search_n10:
	call	finddfcb		; find next matching entry
	 jz	search_next_err		; if not found
	mov	al,attributes		; get attributes that we support
if PASSWORD
	cmp	word ptr DPWD[bx],0	; does it have a password?
	 jz	search_n20		; skip if not
	or	al,DA_HIDDEN		; else include hidden files
search_n20:
endif
	not	al			; attributes we don't support
	and	al,DA_DIR+DA_HIDDEN+DA_SYSTEM+DA_VOLUME
	test	DATTS[bx],al		; any attributes we don't support
	 jnz	search_n10		; then don't count this one
search_n30:
	push ss ! pop es
	mov	di,offset srch_buf	; ES:DI -> DMA search address
	push	di			; save for later
if JOIN
	mov	al,fdos_hds_drv		; save the PHYSICAL drive
else
	mov	al,byte ptr path_drive	; save the specified drive
endif
	inc	al
	stosb
	mov	dx,di			; remember start of name field
	mov	si,offset info_fcb+1	; point at search FCB
	mov	cx,11
	rep	movsb			; save name for search
	mov	al,attributes		; get search attribute
	stosb

	mov	ax,dcnt
	stosw				; save directory count
	mov	ax,fdos_hds_blk		; get the directory block
	stosw				; save the current block

	add	di,4			; skip 4 reserved bytes

	mov	si,dirp			; point to directory name
	test	attributes,DA_CLUSTER	; is the caller requesting
	 jz	search_n35		; the starting cluster ?
	mov	ax,DBLOCK1[si]		; pick it up from dir entry
	dec	di			;  and return at offset 13h
	dec	di			;  in DTA (WARNING - Lantastic
	stosw				; server uses these bytes too..)
search_n35:
    mov cx,32/WORD 
	rep	movsw			;  location (also used by FCB search)

	pop	si			; SI = offset srch_buf
	call	les_di_dmaptr		; ES:DI -> DMA address
	push ss ! pop ds		; DS:SI -> srch_buf
	mov	cx,21
	rep	movsb			; copy from buffer to user DMA
	mov	bx,dirp			; BX -> matching entry
	mov	al,DATTS[bx]
	stosb				; return directory attribute
	lea	si,DTIME[bx]
	movsw ! movsw			; return time, date
	inc	si
	inc	si			; skip starting cluster
	movsw ! movsw			; return file size
	jmp	unparse			; return file name

eject
;	COMMIT FILE (COMMIT)

;	+----+----+----+----+
;	|    50   |  handle |
;	+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle to be flushed

;	exit:
;	-----
;	AX:	0000 or error code ( < 0)

file_updt20:				; NO-OP for clean files
commit_dev:				; NO-OP on character devices
	ret

fdos_commit:
;-----------
	call	vfy_dhndl_ptr		; check file handle
	call	redir_dhndl_offer
	call	local_disk		; get MXdisk, switch stack
	call	verify_handle		; check if legal file handle
	 jc	commit_dev
;	jmp	file_update		; update directory & FAT if written

;	Update directory & File Allocation Table (partial close)
;	entry:	ES:BX -> DHNDL_
;	exit:	ES:BX preserved

	Public	file_update

file_update:
;-----------
	test	es:DHNDL_ATTR[bx],DHAT_CLEAN
	 jnz	file_updt20		; skip if file is clean
	mov	al,es:DHNDL_DCNTLO[bx]	; get directory count
	mov	ah,es:DHNDL_DCNTHI[bx]
	dec	ax
	mov	dcnt,ax			; set search position
	xor	cx,cx			; find any entry
	mov	chdblk,cx		; non-sequential access
	push es ! push bx
	call	getdir			; read the directory entry
	pop bx ! pop es
	test	ax,ax			; did we find something?
	 jz	file_updt10		; skip if directory entry lost
	xchg	ax,di			; DI -> directory entry in buffer
	or	DATTS[di],DA_ARCHIVE	; mark file as modified
;;;	call	timestamp_dhndl		; record the current time
	mov	ax,es:DHNDL_BLK1[bx]	; AX = 1st block in file
	lea	di,DTIME[di]		; DI -> [time,date,block1,size]
	push es ! push bx
	push	ds
    lea si,DHNDL_TIME[bx]
	push ds ! push es
	pop ds ! pop es			; swap ES and DS
	movsw ! movsw			; copy time then date
	stosw				; now 1st cluster
	movsw ! movsw			; finally file size
	pop	ds
	call	update_dir		; update directory
	call	update_fat		; flush dirty FAT
	call	update_dat		; flush all dirty data buffers
	pop	bx ! pop es
	or	es:DHNDL_ATTR[bx],DHAT_CLEAN
	ret				; only mark as clean AFTER it's written

file_updt10:
	mov	ax,ED_H_MATCH		; can't find open file
	mov	fdos_ret,ax		; save error code
	ret



eject
;	CREATE NEW FILE

;	+----+----+----+----+----+----+----+----+
;	|    51   |        name       |  mode   |
;	+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	name:	segmented address of ASCIIZ name
;   mode:   attribute for file 

;	exit:
;	-----
;	AX:	file handle or error code ( < 0)

;	Note:	The function is identical to CREATE FILE
;		with the exception that an error is returned
;		if the specified file already exists.

fdos_mknew:
;----------
	call	redir_asciiz_offer
	call	local_disk		; get MXdisk, switch stack
	call	mustbe_free_handle	; make sure we have spare handle
	call	path_prep_chk		; parse path, walk down the tree
	call	finddfcbf		; check if we can find this one
	 jnz	mknew10
	test	byte ptr fdos_pb+8,DA_VOLUME
	 jz	mknew_nolbl		; skip if not volume label
	call	mustbe_nolbl		; do we have an existing label ?
mknew_nolbl:
	jmp	creat_new_file		; go ahead and create the file
mknew10:
    mov ax,ED_EXISTS        
	jmp	fdos_error

eject
;	LOCK/UNLOCK FILE DATA (LOCK/UNLOCK)

;	+----+----+----+----+----+----+----+----+
;	|    52   |  handle |       offset      |
;	+----+----+----+----+----+----+----+----+
;	|       length      |   lock  |
;	+----+----+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle
;	offset:	long integer offset
;	length:	long integer byte count
;	lock:	0 = lock, 1 = unlock

;	exit:
;	-----
;	AX:	byte count or error code ( < 0)

fdos_lock:
;---------
	call	vfy_dhndl_ptr		; check file handle #
	test	es:DHNDL_WATTR[bx],DHAT_DEV
	 jnz	lock_dev		; skip if character device
	call	redir_dhndl_offer
	call	local_disk		; get MXdisk, switch stack
	call	check_handle		; check if legal file handle
	mov	ax,ED_ACCESS		; assume a problem
	 jc	lock_error		; can't lock/unlock device handles
	mov	di,offset fdos_pb
	callf	ss:share_stub+S_LOCKS	; call the stub routine
	 jnc	lock_ret		; return error if we got one
lock_error:
	jmp	fdos_error		; can't do locking

lock_dev:
	mov	bx,ED_ACCESS
lock_ret:
	ret

eject
;	BUILD DDSC FROM BPB

;	+----+----+----+----+----+----+----+----+----+----+
;	|    53   |       bpbptr      !      ddscptr      |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	bpbptr:		address of BPB
;	ddscptr:	address of DDSC to be built

;	exit:
;	-----
;		0000 or error code ( < 0)

fdos_mkddsc:
;-----------
	push	ds
	mov	si,2[bp]		; SI -> parameter block
	les	di,6[si]		; ES:DI -> DDSC
	lds	si,2[si]		; DS:SI -> BPB

	call	bpb2ddsc		; convert BPB to DDSC
	xor	bx,bx			; no error
	pop	ds			; restore segment register
	ret


bpb2ddsc:
;--------
; build a DDSC from a BPB, leavinf UNIT, RUNIT, DEVICE, FIRST, and LINK fields
; unchanged
; On Entry:
;	DS:SI -> source BPB
;	ES:DI -> destination DDSC
; On Exit:
;	None
	lodsw				; get sector size in bytes
	mov	es:DDSC_SECSIZE[di],ax

	lodsb				; get sectors/allocation unit
	dec	ax			; get cluster mask
	mov	es:DDSC_CLMSK[di],al	; store cluster mask
	lodsw				; get FAT address
	mov	es:DDSC_FATADDR[di],ax

	lodsb				; get # of fats
	mov	es:DDSC_NFATS[di],al
	cbw				; make it a word value
	xchg	ax,dx			; and keep in DX

	lodsw				; get # of directory entries
	mov	es:DDSC_DIRENT[di],ax

	lodsw				; get # of sectors total in image
	push	ax			; save disk size for later

	lodsb				; get FAT id byte
	mov	es:DDSC_MEDIA[di],al	; set media byte

	lodsw				; get # of sectors in a fat
if DOS5
	mov	es:DDSC_NFATRECS[di],ax	; set FAT size
else
	mov	es:DDSC_NFATRECS[di],al	; set FAT size
endif

	mul	dx			; AX = FAT size
	add	ax,es:DDSC_FATADDR[di]	; AX = 1st directory sector
	mov	es:DDSC_DIRADDR[di],ax	; set root directory address

	add	si,word+word+dword	; skip SPT, NHEADS, BIGHIDDEN

	mov	cx,es:DDSC_SECSIZE[di]
	mov	ax,32			; size of single directory entry
	mul	es:DDSC_DIRENT[di]		; AX/DX = root directory bytes
	add	ax,cx			; round up sector size
	dec	ax			;  in case of odd number
	div	cx			; convert to whole sectors
	add	ax,es:DDSC_DIRADDR[di]	; add in base of root directory
	mov	es:DDSC_DATADDR[di],ax	; set this as first cluster address

	pop	ax			; recover total disk size
	xor	dx,dx			; assume 16 bit number
	test	ax,ax			; test for big drive
	 jnz	bpb2ddsc10		; skip if not large drive
	lodsw				; get low word of size
	mov	dx,[si]			; get high word of size
bpb2ddsc10:				; AX/DX = disk size in sectors
	sub	ax,es:DDSC_DATADDR[di]	; subtract non-data portion
	sbb	dx,0

	xor	cx,cx			; CL = cluster shift
	mov	ch,es:DDSC_CLMSK[di]	; CH = cluster mask
bpb2ddsc20:				; count # of 1 bits
	shr	ch,1			; shift right mask
	 jnc	bpb2ddsc30		; skip if all 1 bits shifted out
	inc	cx			; count another 1 bit
	shr	dx,1
	rcr	ax,1			; div by two
	jmps	bpb2ddsc20		; repeat until all 1's counted
bpb2ddsc30:				; CL = log2 (CH)
	mov	es:DDSC_CLSHF[di],cl	; set cluster shift
	inc	ax			; clusters 0/1 reserved (+2), and we
	mov	es:DDSC_NCLSTRS[di],ax	;  want max (-1), so +1
	xor	ax,ax
	mov	es:DDSC_BLOCK[di],ax	; next block = 0
	dec	ax
	mov	es:DDSC_FREE[di],ax	; free space = -1 (unknown)
	ret

eject
;	DIRECT DISK IO

;	+----+----+----+----+----+----+
;	|    54   |drv | op |nsectors |
;	+----+----+----+----+----+----+----+----+
;	|      startsec     |    dma address    |
;	+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	param block set up for direct disk IO 

;	exit:
;	-----
;	AX = 0, or error code
;

DIO_25_READ_OP		equ	1
DIO_26_WRITE_OP		equ	2

fdos_ddio:
;---------
;	Used by emulator for Int 25H/26H disk calls to BIOS
;
;	entry:	dio = offset of parameter block in user data segment
;
;	exit:	AX = return code from BIOS

	call	local_disk		; get the MX
	mov	ax,fdos_pb+WORD		; AX = logical drive
	call	logical2physical	; AX = physical drive
	call	get_ddsc		; make sure this drive is valid
	 jc	fdos_ddio30		; bail if not
	mov	adrive,al		; remember this unit
	mov	ax,CMD_INPUT+0*256	; disk read operation of system area
	cmp	byte ptr fdos_pb+3,DIO_26_WRITE_OP
	 jne	fdos_ddio10
	mov	ax,0FFFFh
	mov	es:DDSC_FREE[bx],ax	; free space = -1 (unknown)
	call	hshdscrd		; discard hashing info for all drives (AL=FF)
	mov	ax,CMD_OUTPUT+1*256	; disk write operation of system area
fdos_ddio10:
	mov	rwmode,ah		; save read/write of system area
	mov	req_hdr+5,ah		; (so driver can get the hint)
	mov	bx,offset req_hdr	; we will build request here
	mov	RH_CMD[bx],al		; save the command
	mov	ax,fdos_pb+4		; AX = # sectors
	mov	RH4_COUNT[bx],ax	; set requested sector count
	les	ax,dword ptr fdos_pb+6	; pick up 32-bit record address
	mov	word ptr pblock,ax
	mov	word ptr pblock+WORD,es
	les	ax,dword ptr fdos_pb+10	; ES:AX -> DMA seg
	mov	RH4_BUFOFF[bx],ax
	mov	RH4_BUFSEG[bx],es
	call	ddioif			; go do it
	 jns	fdos_ddio20		; did we have a problem ?
	cbw				; AX = base error
	add	ax,ED_PROTECT		; "add" in BIOS error code
	mov	fdos_ret,ax		; save return code
fdos_ddio20:
	ret

fdos_ddio30:
	jmp	fdos_ED_DRIVE		; return bad drive error

eject
;	EXPAND FILE NAME

;	+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
;	|    55   |   relative name   |                   |   absolute name   |
;	+----+----+----+----+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	relative name:	segmented address of ASCIIZ name
;	absolute name:	segmented address of ASCIIZ name

;	exit:
;	-----
;	AX = BX: 0000 or error code ( < 0)

fdos_expand:
;-----------
	call	redir_asciiz_dev_offer	; is it a networked device ?
	call	asciiz_dev_offer	; see it we are expanding a device name
	call	local_disk		; get MXdisk, switch stack
	push	ds
	les	si,dword ptr fdos_pb+2	; get relative name buffer
	mov	ax,es:[si]		; get 1st two chars of path
	call	check_dslash		; is it a '\\' form ?
	 je	fdos_expand10
	mov	bx,ED_FILE
    test    al,al           
	 jz	fdos_expand60
	call	toupper			; work on premise we have drive
	sub	al,'A'			; specified in relative name
	sub	ah,':'			; did we have ?
	 je	fdos_expand20		; if so use it
fdos_expand10:
	mov	al,current_dsk		; use current drive
fdos_expand20:
	cmp	al,last_drv		; is it a legal drive
	 jae	fdos_expand50

; we should check for a media change so select_logical_drv seems to be
; a better solution
;	call	get_ldt			; ES:BX -> LDT_ for drive
;	 jc	fdos_expand40		; no LDT at init - copy relative path
;	mov	word ptr current_ldt,bx
;	mov	word ptr current_ldt+WORD,es
	call	select_logical_drv
	les	bx,current_ldt

	test	es:LDT_FLAGS[bx],LFLG_PHYSICAL
	 jz	fdos_expand50		; make sure it's a valid drive
	push ss ! pop es
	mov	di,offset pri_pathname	; build name in pathname buffer
	lds	si,dword ptr fdos_pb+2

; we must trick redir_build_path to find the correct function number -
; otherwise we could not get correct (critical) error reporting to work
	push	bp
	mov	bp,offset fdos_pb+6
	mov	2[bp],bp
	mov	byte ptr [bp],FD_EXPAND

	call	redir_build_path	; build name from LDT
	pop	bp
	 jc	fdos_expand60		; bail out if bad name
	lds	di,ss:current_ldt	; we need to append a '\' if
	mov	bx,ds:LDT_ROOTLEN[di]	;  we are at the root
	test	ds:LDT_FLAGS[di],LFLG_SUBST
	 jz	fdos_expand30		; if drive is SUBST'd then
	mov	bx,2			;  only append if real root
fdos_expand30:
	mov	si,offset pri_pathname	; SS:SI -> full path
	push ss ! pop ds		; leave current ldt -> buffer as LAN
	mov	word ptr current_ldt,si	;  MAN 2.1 extended edition expects it
	mov	word ptr current_ldt+WORD,ds
	mov	ax,'\'			; get pathchar in AX for checks
	cmp	ds:byte ptr [si+bx],ah	; are we at the root ?
	 jne	fdos_expand40		; if so append a '\'
	mov	ds:word ptr [si+bx],ax
fdos_expand40:
	les	di,ss:dword ptr fdos_pb+10; ES:DI -> destination buffer
	call	copy_asciiz		; copy the full pathname
	pop	ds
	ret

fdos_expand50:
	mov	bx,ED_DRIVE		; return bad drive error
fdos_expand60:
	mov	ax,bx
	pop	ds
	jmp	fdos_error

expand_dev:
;----------
	les	si,dword ptr fdos_pb+2	; this is the original source
	lods	es:ax			; get the source
	les	di,dword ptr fdos_pb+10	; data will end up here
	cmp	ah,':'			; is a drive specified ?
	 je	expand_dev10
	mov	al,'A'
	add	al,ss:current_dsk
expand_dev10:
	call	toupper			; make sure drive letter is upper case
	stosb				; plant an 'd'
	mov	ax,':'+256*'/'
	stosw				; make that 'd:/'
	mov	bx,offset name_buf	; DS:BX -> name buffer
	jmp	unparse			; unparse the device name

eject
;	RENAME FILE

;	+----+----+----+----+----+----+----+----+----+----+----+----+----+----+
;	|    56   |      old name     |                   |      new name     |
;	+----+----+----+----+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	old name:	segmented address of ASCIIZ name
;	new name:	segmented address of ASCIIZ name

;	exit:
;	-----
;	AX:	0000 or error code ( < 0)

;	Note:	R/O files can be renamed.
;			  ---

fdos_move:
;---------
	call	redir_move_offer
	call	local_disk		; get MXdisk, switch stack
	call	path_prep		; parse the path, go to bottom level
	call	chk_no_dev
	cmp	ss:remote_call,0	; wildcards allowed if server/FCB
	 jnz	move10
	call	chk_no_wild		; make sure not a wild card
move10:
	call	finddfcbf		; else try to locate directory entry
	 jz	fdos_ED_FILE		; error if no match occurred
move20:
	call	close_if_same_psp	; make sure not open by other PDs
if PASSWORD
	call	check_pwd_any		; check if password protected
endif
	mov	si,dirp			; get matching directory entry
	mov	di,offset save_area	; get a temporary save area
	mov	cx,32/WORD		; save it all
	rep	movsw			; so we can move it to new entry

	mov	si,offset fdos_hds
	mov	di,offset saved_hds
	mov	cl,HDS_LEN
	rep	movsb			; copy current HDS to safe place

	mov	ax,dcnt			; also save directory index
	mov	saved_dcnt,ax		; so we can release entry later

	les	di,dword ptr fdos_pb+10	; es:di -> path name
	call	path_prep_ptr		; find the destination path
	call	chk_no_dev		; no devices allowed here
	cmp	ss:remote_call,0	; wildcards allowed if server/FCB
	 jnz	move25
	call	chk_no_wild		; make sure not a wild card
move25:
	mov	al,fdos_hds_drv		; get the work drive
	cmp	al,saved_hds_drv	; make sure same drive for
	 je	move30			;   source and destination

	mov	ax,ED_DEVICE		; "not same device" error
	jmps	fdos_moverr		; return the error
fdos_ED_FILE:
	mov	ax,ED_FILE		; get error code
fdos_moverr:
	jmp	fdos_error		; return the error

move30:					; same drive for source & destination
	mov	al,byte ptr fdos_pb+8	; check attributes
if PASSWORD
	cmp	save_area+DPWD,0
	 je	move31
	or	al,DA_HIDDEN
move31:
endif
	not	al
	and	al,DA_HIDDEN+DA_SYSTEM+DA_DIR+DA_VOLUME
	test	save_area+DATTS,al
	 jnz	move80
	mov	si,offset info_fcb+1	; SI->new name (possible wildcards)
	mov	di,si			; DI->new name
	mov	bx,offset save_area	; current name on disk
    cmp byte ptr DNAME[bx],'.'  
     je move80          
	mov	cx,11
move40:					; fill in the wild card characters
	lodsb				; get next character from new name
if KANJI
	call	dbcs_lead		; is it the 1st of a kanji pair
	 jne	move45
	dec	cx			; copied this one
	 jcxz	move55			; discard if no room for kanji char
	stosb				; copy 1st byte of Kanji pair
	inc	bx
	lodsb				; copy 2nd byte
	jmps	move50
move45:
endif
	call	toupper			; make it upper case
	cmp	al,'?'			; is it just a wild card?
	 jne	move50			; no, put in destination as is
	mov	al,[bx]			; else leave original character
move50:					; AL = next char for destination
	stosb				; store next char in destination
	inc	bx			; increment all pointers
	loop	move40			; repeat for all 11 characters
move55:

	call	finddfcbf		; find first non-volume entry
	 jnz	move_access		; file already exists, return error

	call	move_comp		; source & destination in same dir?
	 jne	move60			; skip if moving to different directory
	call	move_seek		; else find old entry again
	jmps	move70			; replace name in old entry

move60:					; moving to different directory
	test	save_area+DATTS,DA_DIR	; can't move directory to new path
	 jnz	move_access		; so return error if new path
	call	allocdir		; allocate a directory entry
move70:
	mov	si,offset info_fcb+1	; get pointer to new name
	mov	di,dirp			; get pointer to directory entry
	mov	cx,11			; copy new name into buffer
	rep	movsb


	mov	si,offset save_area+11	; copy remaining info
	mov	cx,32-11
	rep	movsb
	call	update_dir		; update the disk

	call	move_comp		; did we rename across directories?
	 je	move80			; skip if in same directory
	mov	si,offset saved_hds
	mov	di,offset fdos_hds	; else need to copy HDS back
	mov	cx,HDS_LEN		; so we can get old entry again
	rep	movsb			;    (time to get rid of it)
	call	move_seek		; seek the original entry
	mov	bx,dirp
	mov	DNAME[bx],0E5h		; and bye, bye! it goes...
	call	update_dir		; update the old directory entry
move80:
	cmp	ss:remote_call,0	; wildcards allowed if server/FCB
	 jz	move90			;  so check for multiple files
	call	path_prep		; parse the path, go to bottom level

	mov	ax,saved_dcnt		; restore dcnt for remote call
	mov	dcnt,ax			; to function properly
	and	chdblk,0

	call	finddfcb		; try to locate another directory entry
	 jz	move90			; no, return now
	jmp	move20			;  round again if we do

move_access:
	jmp	fdos_ED_ACCESS		; "access denied" if file
					;     already exists
move90:
; in order to update the current directory in case part of it has been
; renamed we call fdos_chdir which does all the work for rebuilding
; the LDT_ for the given drive
	push	ss
	pop	ds
	mov	bx,offset sec_pathname
	mov	ax,path_drive
	add	ax,'A'+(':'*100h)
	mov	[bx],ax
	mov	word ptr 2[bx],'.'
	mov	fdos_pb+4,ds
	mov	fdos_pb+2,bx
	mov	byte ptr remote_call,0
	jmp	fdos_move_chdir

move_seek:				; re-seek the old directory entry
	mov	ax,saved_dcnt		; get saved directory count
	dec	ax			; move back one for GETDIR
	mov	dcnt,ax			; set search offset
	and	chdblk,0		; follow the chains, not sequential
	mov	cl,0			; return next entry
	jmp	getdir			; in current directory

move_comp:
	mov	ax,fdos_hds_blk
	cmp	ax,saved_hds_blk
	ret

eject
;	GET/SET FILE DATE/TIME

;	+----+----+----+----+----+----+----+----+----+----+
;	|    57   |  handle |   mode  |   date  |   time  |
;	+----+----+----+----+----+----+----+----+----+----+

;	entry:
;	------
;	handle:	open file handle
;	mode:	0 = get date/time, 1 = set date/time
;	date:	date as in directory FCB
;	time:	time as in directory FCB

;	exit:
;	-----
;	AX:	0000 or error code ( < 0)
;	date:	date of last modification if mode = 0
;	time:	date of last modification if mode = 0

fdos_dattim:
;-----------
	call	vfy_dhndl_ptr		; check file handle #
	call	redir_dhndl_offer
	call	local_disk		; get MXdisk, switch stack
	call	check_handle		; check if legal file handle
	cmp	fdos_pb+4,0		; get/set date/time?
	 jne	dattim1
	mov	ax,es:DHNDL_DATE[bx]	; get date
	mov	fdos_pb+6,ax
	mov	ax,es:DHNDL_TIME[bx]	; get time
	mov	fdos_pb+8,ax
	ret
dattim1:				; else set date/time stamp
	mov	dx,fdos_pb+6		; set date
	mov	ax,fdos_pb+8		;  and time
	jmps	set_timestamp

	Public	timestamp_dhndl

timestamp_dhndl:
;---------------
; On Entry:
;	ES:BX -> DHNDL_
; On Exit:
;	ES:BX preserved
;
; Mark this DHNDL_ with the current time and date
	push	es
	push	bx
	call	ReadTOD			; get TOD in DX/AX
	pop	bx
	pop	es
;	jmp	set_timestamp

set_timestamp:
;-------------
; On Entry:
;	ES:BX -> DHNDL_
;	AX = time
;	DX = date
; On Exit:
;	None
;
	and	es:DHNDL_ATTR[bx],not DHAT_CLEAN
	mov	es:DHNDL_DATE[bx],dx	; remember to update directory
	mov	es:DHNDL_TIME[bx],ax	; ask SHARE to record the changes
	callf	share_stub+S_RECORD	;  for the benefit of others
	ret


fdos_reopen_fcb:
;---------------
; On entry
; On exit  on success pb offset 6 set to 0 if device, 1 if disk file
	test	byte ptr remote_call+1,DHM_FCB/100h
	jnz	fcb_reopen0
	mov	ax,ED_FUNCTION
	jmp	fdos_error
fcb_reopen0:
	call	local_disk
	call	path_prep
	 jc	fcb_reopen_error	; any error means reopen is invalid
	call	check_device
	 jnc	fcb_reopen_error	; we should not find devices here
	mov	ax,fdos_pb+6
	mov	fdos_hds_blk,ax
	mov	ax,fdos_pb+8
	dec	ax
	mov	dcnt,ax
	xor	cx,cx
	mov	chdblk,cx
	call	getdir
	 jz	fcb_reopen_error
	cmp	DNAME[bx],0e5h		; has the entry been deleted ?
	 je	fcb_reopen_error
	mov	fdos_pb+2,ax
	ret
fcb_reopen_error:
	mov	ax,ED_NOFCBS
	jmp	fdos_error
