; =============================================================================
;
;                               Litos8 files
;
; =============================================================================

; -----------------------------------------------------------------------------
; Exported user functions (46):
;	GetCurPath - get current path
;	SetCurPath - set current path
;	AbsPath - absolutize path
;	OpenFile - open file
;	CreateFileDir - create and open file or directory
;	CreateFile - create and open file
;	CreateDir - create directory
;	DeleteFileDir - delete file or directory
;	DeleteFile - delete file
;	DeleteDir - delete directory
;	RenameFileDir - rename file or directory
;	MoveFile - move file
;	ReadFile - read from file
;	WriteFile - write to file
;	FlushFile - flush file writes
;	CloseFile - close file
;	OpenDir - open directory
;	FindFiles - search files or directories
;	FileExist - check file or directory if exists
;	FileAttr - get file attributes from FILE structure
;	GetFileAttr - get file attributes
;	SetFileAttr - set file attributes
;	FileSize - get file size from FILE structure
;	GetFileSize - get file size
;	SetFileSize - set file size
;	FileDateTime - get file date and time from FILE structure
;	GetFileDateTime - get file date and time
;	SetFileDateTime - set file date and time
;	FilePos - get file seek position
;	FileSeek - set file seek position
;	FileSetPos - set file seek position from begin
;	FileReset - reset file position to begin
;	AddFileName - add filename from FILE structure
;	OpenRoot - open root directory
;	ClearFileDesc - clear FILE descriptor
;	GetClustSect - get first absolute sector of cluster
;	PrepFileMask - prepare filename mask
;	FileSDD - get SDD descriptor from FILE structure
;	GetNextClust - get next FAT entry
;	SetNextClust - set next FAT entry
;	FindFreeClust - find free cluster
;	SetLastFree - set last free cluster
;	ReadFAT - read FAT sector into cache
;	WriteFAT - write FAT sector from cache
;	FlushFAT - write FAT sector, if it was modified
;	DiskFree - get free space on disk
; -----------------------------------------------------------------------------

		CODEINIT_SECTION

; -----------------------------------------------------------------------------
;                       INIT: Initialize current path
; -----------------------------------------------------------------------------

; ------------- initialize last free cluster

CurPathInit:	mov	byte [LastFreeClust],2 ; set last free cluster

; ------------- clear current path

		mov	di,CurPathFILE	; DI <- FILE descriptor
		call	ClearFileDesc	; clear FILE descriptor

; ------------- find boot disk

		mov	cx,[SDDNum]	; CX <- number of system disks
		jcxz	CurPathInit9	; no system disk
		mov	bx,SDDTab	; BX <- system disk tables
		mov	al,[BootDisk]	; AL <- boot disk
CurPathInit2:	mov	si,[bx+SDD_BDD]	; SI <- BDD descriptor
		cmp	al,[si+BDD_Disk]; check BIOS disk number
		je	short CurPathInit4 ; disk has been found
		add	bx,SDD_size	; BX <- next BDD descriptor
		loop	CurPathInit2	; check next BDD descriptor

; ------------- not found, use first fixed disk

		mov	cx,[SDDNum]	; CX <- number of system disks
		mov	bx,SDDTab	; BX <- system disk tables
CurPathInit3:	test	byte [bx+SDD_FlagsDisk],SDDF_Valid ; valid format?
		jnz	short CurPathInit4 ; use this fixed disk
		add	bx,SDD_size	; BX <- next BDD descriptor
		loop	CurPathInit3	; check next BDD descriptor
		mov	bx,SDDTab	; BX <- use first disk

; ------------- fill up FILE structure

CurPathInit4:	mov	byte [di+FILE_Name],PATHCHAR ; mark root directory
		mov	al,[bx+SDD_FlagsDisk] ; AL <- flags and disk
		and	al,SDDF_DiskMask ; AL <- system disk
		push	ax		; push AX (AL=system disk)
		shl	al,1
		shl	al,1
		shl	al,1
		shl	al,1		; AL <- shift disk to bit 4 position
		mov	byte [di+FILE_DirInx],al ; set device type and index
		mov	byte [di+FILE_Attr],DIR_DIR ; set directory attribute
		mov	ah,[bx+SDD_RootSect] ; AH <- number of root sectors
		mov	al,0
		shl	ax,1		; AX <- root size
		mov	[di+FILE_Size],ax ; root directory size

; ------------- set current path text

		pop	ax		; pop AX (AL=system disk)
		add	al,"A"		; AL <- disk letter
		mov	bx,CurPath	; BX <- current path
		mov	byte [bx+TEXT_Length],3 ; size of current path text
		mov	[bx+TEXT_Text],al ; set disk letter
		mov	word [bx+TEXT_Text+1],":" + PATHCHAR*256 ; ":/" chars
CurPathInit9:	ret


		CODE_SECTION

; -----------------------------------------------------------------------------
;                           Read FAT sector into cache
; -----------------------------------------------------------------------------
; INPUT: 	AX = relative FAT sector
;		BX = pointer to SDD descriptor
; OUTPUT:	SI = sector buffer SectBuf
;		CY = read error
; NOTES:	FAT buffer will be flushed if other FAT sector is needed.
; -----------------------------------------------------------------------------

; ------------- push registers

ReadFAT:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX

; ------------- prepare number of FAT tables -> CX

		mov	ch,0		; CH <- 0
		mov	cl,[bx+SDD_FatNum] ; CX <- number of FAT tables

; ------------- recalculate to absolute sector -> DX:AX

		add	ax,[bx+SDD_ResSect] ; add reserved sectors (boot)
		cwd			; DX:AX <- sectors
		add	ax,[bx+SDD_StartSect] ; add absolute sector LOW
		adc	dx,[bx+SDD_StartSect+2] ; add absolute sector HIGH

; ------------- flush old FAT sector, if needed to be changed

		cmp	ax,[LastFATSect] ; check FAT sector LOW
		jne	short ReadFAT2	; need to be changed
		cmp	dx,[LastFATSect+2] ; check FAT sector HIGH
		je	short ReadFAT6	; FAT sector need not to be changed
ReadFAT2:	call	FlushFAT	; flush old FAT sector

; ------------- save last FAT sector

		mov	[LastFATSect],ax ; last FAT sector LOW
		mov	[LastFATSect+2],dx ; last FAT sector HIGH

; ------------- read FAT sector DX:AX to cache

ReadFAT6:	call	SDiskReadSect	; read cached sector
		jnc	short ReadFAT8	; OK

; ------------- next FAT table

		add	ax,[bx+SDD_FatSect] ; shift to next FAT table
		adc	dx,byte 0	; carry
		loop	ReadFAT6	; try next FAT table
		stc			; set error flag

; ------------- pop registers

ReadFAT8:	pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;             Write last FAT sector from cache, if it was modified
; -----------------------------------------------------------------------------
; INPUT: 	BX = pointer to SDD descriptor
; OUTPUT:	CY = write error (all FAT copies are invalid)
; -----------------------------------------------------------------------------

FlushFAT:	cmp	byte [ModiFAT],0 ; was FAT sector modified?
		je	short WriteFAT9	; FAT sector was not modified

; === WriteFAT function must follow

; -----------------------------------------------------------------------------
;                        Write last FAT sector from cache
; -----------------------------------------------------------------------------
; INPUT: 	BX = pointer to SDD descriptor
; OUTPUT:	CY = write error (all FAT copies are invalid)
; NOTES:	FlushFAT should be called after all writes.
; -----------------------------------------------------------------------------

; ------------- push registers

WriteFAT:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	bp		; push BP

; ------------- prepare number of FAT tables -> CX

		mov	byte [ModiFAT],0 ; FAT sector was not modified
		mov	ch,0		; CH <- 0
		mov	cl,[bx+SDD_FatNum] ; CX <- number of FAT tables
		mov	bp,cx		; BP <- error counter

; ------------- get last FAT sector

		mov	ax,[bx+SDD_BDD]	; AX <- BDD descriptor
		mov	[OldSectBDD],ax	; set BDD descriptor
		mov	ax,[LastFATSect] ; AX <- last FAT sector LOW
		mov	dx,[LastFATSect+2] ; DX <- last FAT sector HIGH

; ------------- write FAT sector DX:AX from cache

WriteFAT4:	mov	[OldSect],ax	; set sector LOW
		mov	[OldSect+2],dx	; set sector HIGH
		call	BDiskWriteSect	; write cached sector
		sbb	bp,byte 0	; error counter

; ------------- next FAT table

		add	ax,[bx+SDD_FatSect] ; shift to next FAT table
		adc	dx,byte 0	; carry
		loop	WriteFAT4	; try next FAT table
		cmp	bp,byte 1	; check number of errors
	
; ------------- pop registers

		pop	bp		; pop BP
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
WriteFAT9:	ret

; -----------------------------------------------------------------------------
;                             Get next FAT entry
; -----------------------------------------------------------------------------
; INPUT:	AX = current cluster
;		BX = pointer to SDD descriptor
; OUTPUT:	AX = new cluster (-1 = end of file)
;		CY = error (AX undefined)
; -----------------------------------------------------------------------------

; ------------- push registers

GetNextClust:	push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI

; ------------- check if use FAT16

		test	byte [bx+SDD_FlagsDisk],SDDF_FAT12 ; FAT12?
		jnz	short GetNextClust2 ; yes, FAT12

; ------------- FAT16: prepare sector number -> AX and offset in sector -> DI

		mov	di,ax		; DI <- current cluster
		shl	di,1		; DI <- cluster * 2
		and	di,SECTSIZE-1	; mask offset in sector
		mov	al,ah		; AL <- cluster/256
		mov	ah,0		; AX <- sector with this entry

; ------------- read sector

		call	ReadFAT		; read FAT sector
		jc	short GetNextClust9 ; error

; ------------- get FAT16 entry -> AX

		add	si,di		; SI <- entry offset
		lodsw			; load entry
		cmp	ax,FAT16_ENDMIN	; end of file?
		jae	short GetNextClust7 ; end of file
		jmp	short GetNextClust8

; ------------- prepare sector number -> AX and offset in sector -> DI

GetNextClust2:	mov	cx,ax		; CX <- cluster
		mov	dx,3		; DX <- 3 nibbles per entry
		mul	dx		; DX:AX <- nibble index
		shr	dx,1
		rcr	ax,1		; DX:AX <- nibble index / 2
		mov	di,ax		; DI <- offset
		and	di,SECTSIZE-1	; DI <- offset in sector
		mov	al,ah
		mov	ah,dl
		mov	dh,0		; DX:AX <- offset/256
		shr	dx,1
		rcr	ax,1		; AX <- sector number

; ------------- read sector for 1st byte

		call	ReadFAT		; read FAT sector
		jc	short GetNextClust9 ; error

; ------------- get first byte of entry -> DL

		add	si,di		; SI <- byte address
		mov	dl,[si]		; DL <- first byte of FAT12 entry
		sub	si,di		; SI <- sector buffer

; ------------- sector number -> AX and offset -> SI of 2nd byte

		inc	di		; increase offset in sector
		cmp	di,SECTSIZE	; is offset valid?
		jb	short GetNextClust4 ; offset is valid

; ------------- read sector for 2nd byte

		xor	di,di		; DI <- new offset
		inc	ax		; increase sector
		call	ReadFAT		; read FAT sector
		jc	short GetNextClust9 ; error

; ------------- get second byte of entry -> DH

GetNextClust4:	add	si,di		; SI <- byte address
		mov	dh,[si]		; DH <- second byte of FAT12 entry

; ------------- rotate and mask entry

		xchg	ax,dx		; AX <- 2 bytes of entry
		shr	cx,1		; odd entry?
		jnc	short GetNextClust6 ; even entry
		mov	cl,4		; CL <- number of rotations
		shr	ax,cl		; AX <- rotate entry
GetNextClust6:	and	ax,0fffh	; AX <- mask entry
		cmp	ax,FAT12_ENDMIN	; end of file?
		jb	short GetNextClust8 ; valid cluster

; ------------- end of file

GetNextClust7:	xor	ax,ax		; AX <- 0
		dec	ax		; AX <- -1
GetNextClust8:	clc			; operation OK

; ------------- pop registers

GetNextClust9:	pop	di		; pop SI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                             Set FAT entry
; -----------------------------------------------------------------------------
; INPUT:	AX = next cluster (use FAT16 constants)
;		DX = current cluster
;		BX = pointer to SDD descriptor
; OUTPUT:	CY = error
; NOTES:	FlushFAT need to be called after FAT operations.
; -----------------------------------------------------------------------------

; ------------- push registers

SetNextClust:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI
		push	bp		; push BP
		xchg	ax,bp		; BP <- next cluster

; ------------- check if use FAT16

		test	byte [bx+SDD_FlagsDisk],SDDF_FAT12 ; FAT12?
		jnz	short SetNextClust2 ; yes, FAT12

; ------------- FAT16: prepare sector number -> AX and offset in sector -> DI

		mov	di,dx		; DI <- current cluster
		shl	di,1		; DI <- cluster * 2
		and	di,SECTSIZE-1	; mask offset in sector
		mov	al,dh		; AL <- cluster/256
		mov	ah,0		; AX <- sector with this entry

; ------------- read sector

		call	ReadFAT		; read FAT sector
		jc	short SetNextClust9 ; error

; ------------- set FAT16 entry

		add	di,si		; DI <- entry offset
		xchg	ax,bp		; AX <- next cluster
		stosw			; set entry
		jmp	short SetNextClust8 ; here is NC

; ------------- prepare new cluster -> BP

SetNextClust2:	mov	ch,dl		; CH <- cluster LOW
		mov	cl,4		; CL <- number of rotations
		and	bp,0fffh	; BP <- mask new cluster
		test	ch,1		; odd cluster?
		jz	short SetNextClust3 ; even cluster
		shl	bp,cl		; rotate cluster to 2nd nibble

; ------------- prepare sector number -> AX and offset in sector -> DI

SetNextClust3:	mov	ax,3		; AX <- 3 nibbles per entry
		mul	dx		; DX:AX <- nibble index
		shr	dx,1
		rcr	ax,1		; DX:AX <- nibble index / 2
		mov	di,ax		; DI <- offset
		and	di,SECTSIZE-1	; DI <- offset in sector
		mov	al,ah
		mov	ah,dl
		mov	dl,dh
		mov	dh,0		; DX:AX <- offset/256
		shr	dx,1
		rcr	ax,1		; AX <- sector number

; ------------- read sector for 1st byte

		call	ReadFAT		; read FAT sector
		jc	short SetNextClust8 ; error

; ------------- set first byte of FAT12 entry

		mov	dx,bp		; DX <- next cluster
		add	si,di		; SI <- byte address
		and	byte [si],0fh	; mask old value
		or	[si],dl		; set first byte of FAT12 entry
		test	ch,1		; odd cluster?
		jnz	short SetNextClust4 ; odd cluster
		mov	[si],dl		; set first byte of FAT12 entry
SetNextClust4:	sub	si,di		; SI <- sector buffer
		mov	byte [ModiFAT],1 ; set modified flag

; ------------- sector number -> AX and offset -> SI of 2nd byte

		inc	di		; increase offset in sector
		cmp	di,SECTSIZE	; is offset valid?
		jb	short SetNextClust5 ; offset is valid

; ------------- read sector for 2nd byte

		xor	di,di		; DI <- new offset
		inc	ax		; increase sector
		call	ReadFAT		; read FAT sector
		jc	short SetNextClust9 ; error

; ------------- get second byte of entry -> AL

SetNextClust5:	add	si,di		; SI <- byte address
		xchg	ax,bp		; AX <- next cluster
		and	byte [si],0f0h	; mask old value
		or	[si],ah		; set second byte of FAT12 entry
		test	ch,1		; odd cluster?
		jz	short SetNextClust8 ; even cluster
		mov	[si],ah		; set second byte of FAT12 entry
SetNextClust8:	mov	byte [ModiFAT],1 ; set modified flag

; ------------- pop registers

SetNextClust9:	pop	bp		; pop BP
		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                            Find free cluster
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to SDD descriptor
; OUTPUT:	AX = free cluster
;		CY = error (AX undefined)
; NOTES:	Searching from LastFreeClust position.
; -----------------------------------------------------------------------------

; ------------- push registers

FindFreeClust:	push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- prepare registers

		mov	cx,[bx+SDD_ClustNum] ; CX <- number of clusters
		mov	si,cx		; SI <- number of clusters
		mov	dx,[LastFreeClust] ; DX <- last free cluster
		inc	si		; SI <- last cluster index

; ------------- increase cluster index

FindFreeClust2:	inc	dx		; DX <- next cluster
		cmp	dx,si		; check end of disk
		jbe	short FindFreeClust4 ; cluster number is OK
		mov	dx,2		; DX <- wrap to start of disk

; ------------- check, if this cluster is free

FindFreeClust4:	mov	ax,dx		; AX <- current cluster
		call	GetNextClust	; get next FAT entry -> AX
		jc	short FindFreeClust9 ; error
		or	ax,ax		; free cluster has been found?
		loopnz	FindFreeClust2	; check next cluster
		stc			; set error flag
		jnz	short FindFreeClust9 ; free cluster not found

; ------------- free cluster has been found

		xchg	ax,dx		; AX <- free cluster
		mov	[LastFreeClust],ax ; save new last free cluster
		clc			; clear error flag

; ------------- pop registers

FindFreeClust9:	pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                         Set last free cluster
; -----------------------------------------------------------------------------
; INPUT:	AX = last free cluster (2..)
;		BX = pointer to SDD descriptor
; NOTES:	Used by FindFreeClust function.
; -----------------------------------------------------------------------------

SetLastFree:	mov	[LastFreeClust],ax ; set last free cluster
		ret

; -----------------------------------------------------------------------------
;                          Get free space on disk
; -----------------------------------------------------------------------------
; INPUT:	AL = disk number (0..)
; OUTPUT:	DX:AX = free space on disk (in bytes, 0 on error)
;		BX:CX = used space on disk (in bytes, 0 on error)
;		CY = error (DX:AX, BX:CX = 0 on error)
; -----------------------------------------------------------------------------

; ------------- push registers

DiskFree:	push	si		; push SI

; ------------- get system disk descriptor -> BX

		call	SDiskGetDesc	; get system disk descriptor -> BX
		jc	short DiskFree8	; error

; ------------- get free clusters -> SI

		mov	dx,FAT16_MIN	; DX <- start cluster
		mov	cx,[bx+SDD_ClustNum] ; CX <- number of clusters
		xor	si,si		; SI <- counter of free clusters
DiskFree2:	mov	ax,dx		; AX <- cluster
		call	GetNextClust	; get next cluster -> AX
		jc	short DiskFree8	; error
		cmp	ax,byte 1	; free cluster?
		adc	si,byte 0	; increase counter of free clusters
		inc	dx		; DX <- next cluster
		loop	DiskFree2	; next cluster

; ------------- calculate used space -> BX:CX and free space -> DX:AX

		mov	cx,[bx+SDD_ClustSize] ; CX <- cluster size
		mov	ax,[bx+SDD_ClustNum] ; AX <- number of clusters
		sub	ax,si		; AX <- used clusters
		mul	cx		; DX:AX <- used space
		xchg	ax,cx		; CX<-used space LOW, AX<-cluster size
		mov	bx,dx		; BX <- used space HIGH
		mul	si		; DX:AX <- free space
		clc			; clear error flag
		jmp	short DiskFree9

; ------------- error

DiskFree8:	xor	ax,ax		; AX <- 0
		xor	bx,bx		; BX <- 0
		xor	cx,cx		; CX <- 0
		xor	dx,dx		; DX <- 0
		stc			; set error flag

; ------------- pop registers

DiskFree9:	pop	si		; pop SI
		ret

; -----------------------------------------------------------------------------
;                     Get system disk descriptor from FILE structure
; -----------------------------------------------------------------------------
; INPUT:	DI = FILE structure
; OUTPUT:	BX = pointer to SDD descriptor (or NULL on CY error)
;		CY = error, invalid disk number (returns BX = NULL)
; -----------------------------------------------------------------------------

FileSDD:	push	ax		; push AX

		test	byte [di+FILE_Attr],DIR_Device ; device?
		jz	short FileSDD2	; not device
		test	byte [di+FILE_DirInx],FILEDEV_MASKTYPE ; system disk?
		stc			; set error flag
		jnz	short FileSDD6	; error, this is not system disk

FileSDD2:	mov	al,[di+FILE_DirInx] ; AL <- disk
		shr	al,1
		shr	al,1
		shr	al,1
		shr	al,1		; AL <- system disk
		call	SDiskGetDesc	; get system disk descriptor -> BX

FileSDD6:	pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                           Prepare filename mask
; -----------------------------------------------------------------------------
; INPUT:	CX = filename length
;		SI = filename
;		DI = file mask buffer (length 11: 8 name + 3 extension)
; OUTPUT:	CY = mask contains "?" characters
; -----------------------------------------------------------------------------

; ------------- push registers

PrepFileMask:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	bp		; push BP
		mov	bp,di		; BP <- push DI

; ------------- clear filename mask

		push	cx		; push CX
		mov	al," "		; AL <- space
		mov	cx,11		; CX <- filename length
		rep	stosb		; clear filename mask
		pop	cx		; pop CX

; ------------- special case "."

		mov	di,bp		; DI <- destination buffer
		cmp	cx,byte 1	; 1 character?
		jne	short PrepFileMask1
		cmp	byte [si],"."	; "." directory?
		jne	short PrepFileMask1
		mov	byte [di],"."	; set mask to "."
		jmp	short PrepFileMask8

; ------------- special case ".."

PrepFileMask1:	cmp	cx,byte 2	; 2 characters?
		jne	short PrepFileMask2
		cmp	word [si],".."	; ".." directory?
		jne	short PrepFileMask2
		mov	word [di],".."	; set mask to ".."
		jmp	short PrepFileMask8

; ------------- filename mask - name part

PrepFileMask2:	lea	dx,[bp+8]	; DX <- end of name
PrepFileMask22:	jcxz	PrepFileMask8	; no character left
		lodsb			; AL <- load one character
		call	UpperCase	; AL <- convert to upper case letter
		dec	cx		; decrement character counter
		cmp	al,"."		; extension?
		je	short PrepFileMask5 ; extension
		cmp	al,"*"		; asterisk?
		jne	short PrepFileMask3 ; no
		mov	al,"?"		; substitute with "?"
		dec	si		; return character
		inc	cx		; return character counter
PrepFileMask3:	stosb			; store one character
		cmp	di,dx		; end of name?
		jne	short PrepFileMask22 ; next character

; ------------- skip to extension

PrepFileMask4:	jcxz	PrepFileMask8	; no character left
		lodsb			; AL <- load one character
		dec	cx		; decrement character counter
		cmp	al,"."		; extension?
		jne	short PrepFileMask4		

; ------------- prepare search filename mask - extension part

PrepFileMask5:	mov	di,dx		; DI <- buffer with mask, extension
		lea	dx,[bp+11]	; DX <- end of extension
PrepFileMask6:	jcxz	PrepFileMask8	; no character left
		lodsb			; load one character
		call	UpperCase	; AL <- convert to upper case letter
		dec	cx		; decrement character counter
		cmp	al,"."		; extension?
		je	short PrepFileMask8 ; invalid character
		cmp	al,"*"		; asterisk?
		jne	short PrepFileMask7 ; no
		mov	al,"?"		; substitute with "?"
		dec	si		; return character
		inc	cx		; return character counter
PrepFileMask7:	stosb			; store one character
		cmp	di,dx		; end of extension?
		jne	short PrepFileMask6 ; next character

; ------------- check "?" characters

PrepFileMask8:	mov	di,bp		; DI <- mask buffer
		mov	cx,11		; CX <- mask length
		mov	al,"?"		; AL <- character to search
		repne	scasb		; find "?" character
		clc			; clear error flag
		jne	short PrepFileMask9 ; "?" character not found
		stc			; set error flag

; ------------- pop registers

PrepFileMask9:	mov	di,bp		; DI <- pop DI
		pop	bp		; pop BP
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                    Get first absolute sector of cluster
; -----------------------------------------------------------------------------
; INPUT:	AX = cluster (0=root)
;		BX = pointer to SDD descriptor
; OUTPUT:	DX:AX = absolute sector
; -----------------------------------------------------------------------------

; ------------- push registers

GetClustSect:	push	cx		; push CX

; ------------- root directory

		or	ax,ax		; root ?
		jnz	short GetClustSect2 ; no
		mov	ax,[bx+SDD_RootStart] ; AX <- root start LOW
		mov	dx,[bx+SDD_RootStart+2] ; DX <- root start HIGH
		jmp	short GetClustSect4

; ------------- calculate sector

GetClustSect2:	dec	ax
		dec	ax		; AX <- cluster - 2
		mov	ch,0		; CH <- 0
		mov	cl,[bx+SDD_SectClust] ; CX <- sectors per cluster
		mul	cx		; DX:AX <- offset of cluster
		add	ax,[bx+SDD_ClustStart] ; AX <- add data start LOW
		adc	dx,[bx+SDD_ClustStart+2] ; DX <- add data start HIGH

; ------------- pop registers

GetClustSect4:	pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                           Load directory entry
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = absolute sector
;		BX = pointer to SDD descriptor
;		SI = pointer to DIR entry in SectBuf
;		DI = pointer to FILE structure
; OUTPUT:	DI = pointer to next FILE structure
; DESTROYS:	SI
; -----------------------------------------------------------------------------

; ------------- push registers

LoadDirEntry:	push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX

; ------------- copy entry

		mov	cx,DIR_size/2	; CX <- directory entry size/2
		rep	movsw		; copy directory entry

; ------------- clear current offset and cluster

		mov	[di-32+FILE_ClustFile],cx;clear current cluster in file
		mov	[di-32+FILE_ClustOff],cx ; clear offset in cluster

; ------------- relative sector with directory entry

		sub	ax,[bx+SDD_StartSect] ; AX <- relative sector LOW
		sbb	dx,[bx+SDD_StartSect+2] ; DX <- relative sector HIGH
		mov	[di-32+FILE_DirL],ax ; dir entry LOW
		mov	[di-32+FILE_DirH],dl ; dir entry HIGH

; ------------- dir entry in sector and disk

		sub	si,SectBuf+DIR_size ; SI <- entry offset
		mov	cl,5		; CL <- number of rotations
		shr	si,cl		; DI <- entry index
		mov	ch,[bx+SDD_FlagsDisk] ; CH <- system disk
		and	ch,SDDF_DiskMask ; CH <- mask system disk
		dec	cx		; CL <- 4
		shl	ch,cl		; CH <- disk to position 4
		mov	cl,ch		; CL <- disk
		or	cx,si		; CL <- add entry index
		mov	[di-32+FILE_DirInx],cl ; dir entry in sector

; ------------- start absolute cluster

		mov	ax,[di-32+FILE_Cluster] ; AX <- start cluster
		mov	[di-32+FILE_ClustDisk],ax ; set current abs. cluster

; ------------- pop registers

		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                           Find entry in directory
; -----------------------------------------------------------------------------
; INPUT:	AX = first cluster of directory (0=root)
;		BX = pointer to SDD descriptor
;		CX = filename length (0=find first unused DIR entry)
;		DL = required attributes (incl. DIR_THISDIR and DIR_UPDIR)
;		DH = mask of attributes (incl. DIR_THISDIR and DIR_UPDIR)
;		SI = filename (may contain "*" and "?")
;		DI = array of FILE structures (NULL = only get number of files)
;		BP = max. number of files (0=no limit)
; OUTPUT:	BP = number of files
;		CY = error, file not found or some error
; NOTES:	Deleted entry is found only if mask starts with FATDELCHAR.
; -----------------------------------------------------------------------------
; Local variables:
%define FDE_ATTRMASK	[bp+13]		; mask of attributes
%define FDE_ATTRREQ	[bp+12]		; required attributes
%define FDE_FILEREM	[bp+6]		; counter of remaining files
%define FDE_FILEN	[bp+4]		; counter of files
%define FDE_CLUSTER	[bp+2]		; current cluster
%define FDE_FILE	[bp+0]		; pointer to FILE structure (NULL=none)

; ------------- push registers

FindDirEntry:	push	ax		; [bp+16] push AX
		push	cx		; [bp+14] push CX
		push	dx		; [bp+12] push DX FDE_ATTR
		push	si		; [bp+10] push SI
		push	di		; [bp+8] push DI
		or	bp,bp		; no limit?
		jnz	short FindDirEntry1 ; no
		dec	bp		; BP <- -1, no limit
FindDirEntry1:	push	bp		; [bp+6] push BP FDE_FILEREM
		xor	dx,dx		; DX <- 0
		push	dx		; [bp+4] FDE_FILEN
		push	ax		; [bp+2] push AX FDE_CLUSTER
		push	di		; [bp+0] push DI FDE_FILE
		mov	bp,sp		; BP <- local variables

; ------------- prepare file mask SFileMask

		mov	di,SFileMask	; DI <- file mask buffer
		call	PrepFileMask	; prepare file mask
		or	cx,cx		; empty file name?
		jnz	short FindDirEntry2 ; no
		mov	byte [SFileMask],0 ; mark empty file name

; ------------- prepare to search one cluster

FindDirEntry2:	mov	ax,FDE_CLUSTER	; AX <- cluster
		call	GetClustSect	; DX:AX <- absolute sector
		mov	ch,0		; CH <- 0
		mov	cl,[bx+SDD_SectClust] ; CX <- sectors per cluster
		
; ------------- load one sector

FindDirEntry3:	call	SDiskReadSect	; read cached sector
		jnc	short FindDirEntry32 ; ok
		jmp	FindDirEntry9	; error
FindDirEntry32:	mov	di,si		; DI <- sector

; ------------- find file in one sector

FindDirEntry4:	mov	si,SFileMask	; SI <- filename mask

		cmp	byte [di],0	; end of directory?
		jne	short FindDirEntry40 ; not end of directory
		cmp	byte [si],0	; search unused entry?
		je	short FindDirEntry47 ; found unused entry
		jmp	FindDirEntry94	; no

FindDirEntry40:	cmp	byte [di],FATDELCHAR ; deleted entry?
		jne	short FindDirEntry41 ; no
		cmp	byte [si],FATDELCHAR ; required deleted entry?
		jne	short FindDirEntry5 ; skip this entry

FindDirEntry41:	push	ax		; push AX (sector LOW)
		push	cx		; push CX (sector counter)
		push	di		; push DI (sector pointer)

		mov	al,[di+DIR_Attr] ; AL <- attributes
		and	al,DIR_Mask	; mask attributes
		cmp	word [di],". "	; this directory?
		jne	short FindDirEntry42 ; no
		or	al,DIR_THISDIR	; set "this directory" flag
FindDirEntry42:	cmp	word [di],".."	; upper directory?
		jne	short FindDirEntry43 ; no
		or	al,DIR_UPDIR	; set "up directory" flag

FindDirEntry43:	and	al,FDE_ATTRMASK	; mask attributes
		cmp	al,FDE_ATTRREQ	; check attributes
		jne	FindDirEntry46	; attributes do not suit

		mov	cl,11		; CX <- filename length
FindDirEntry44:	lodsb			; AL <- character from mask
		inc	di		; increase pointer
		cmp	al,"?"		; check this character?
		je	short FindDirEntry45 ; don't check
		dec	di		; decrease pointer
		scasb			; check character
FindDirEntry45:	loope	FindDirEntry44	; check next character

FindDirEntry46:	pop	di		; pop DI (sector pointer)
		pop	cx		; pop CX (sector counter)
		pop	ax		; pop AX (sector LOW)
		jne	short FindDirEntry5 ; entry not found

; ------------- get this entry

FindDirEntry47:	inc	word FDE_FILEN	; increase counter of files
		mov	si,FDE_FILE	; SI <- FILE pointer
		or	si,si		; check FILE pointer
		jz	short FindDirEntry48 ; only get number of files

		push	di		; push DI (sector pointer)
		xchg	si,di		; SI <- DIR entry, DI <- FILE entry
		call	LoadDirEntry	; load directory entry
		mov	FDE_FILE,di	; next FILE entry
		pop	di		; pop DI

FindDirEntry48:	dec	word FDE_FILEREM ; file counter
		jz	short FindDirEntry94 ; end of search

; ------------- next entry in sector

FindDirEntry5:	lea	di,[di+DIR_size] ; DI <- next entry
		cmp	di,SectBuf+SECTSIZE ; end of sector?
		jb	short FindDirEntry4 ; try next entry

; ------------- next sector in cluster

		inc	ax		; increase sector LOW
		jnz	short FindDirEntry6 ; no carry
		inc	dx		; carry
FindDirEntry6:	loop	FindDirEntry3	; try next sector

; ------------- move to next directory cluster

FindDirEntry7:	mov	ax,FDE_CLUSTER	; AX <- cluster
		call	GetNextClust	; get next cluster
		jc	short FindDirEntry9 ; error
		mov	FDE_CLUSTER,ax 	; BP <- cluster
		inc	ax		; end of directory?
		jz	short FindDirEntry94 ; end of directory
		jmp	FindDirEntry2	; next cluster
FindDirEntry94:	cmp	word FDE_FILEN,byte 1 ; check number of files

; ------------- pop registers

FindDirEntry9:	pop	bp		; destroy FDE_FILE
		pop	bp		; destroy FDE_CLUSTER
		pop	bp		; pop BP (number of entries FDE_FILEN)
		pop	di		; destroy FDE_FILEREM
		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

%undef FDE_ATTRMASK
%undef FDE_ATTRREQ
%undef FDE_FILEREM
%undef FDE_FILEN
%undef FDE_CLUSTER
%undef FDE_FILE

; -----------------------------------------------------------------------------
;                              Clear FILE descriptor
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure
; -----------------------------------------------------------------------------

ClearFileDesc:	push	ax		; push AX
		push	cx		; push CX
		push	di		; push DI

		mov	ax,"  "		; AX <- space character
		mov	cx,5		; CX <- name length / 2
		rep	stosw		; clear file name
		stosb			; clear last character

		xor	ax,ax		; AX <- 0
		stosb			; clear first byte
		mov	cx,(FILE_size-12)/2 ; CX <- size of FILE structure / 2
		rep	stosw		; clear FILE structure

		pop	di		; pop DI
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                              Open root directory
; -----------------------------------------------------------------------------
; INPUT:	AL = system disk (0..)
;		DI = pointer to destination FILE structure
; OUTPUT:	CY = error
; -----------------------------------------------------------------------------

; ------------- push registers

OpenRoot:	push	ax		; push AX
		push	bx		; push BX

; ------------- get SDD descriptor -> BX

		call	SDiskGetDesc	; get SDD descriptor
		jc	short OpenRoot9	; invalid system disk

; ------------- reload system disk info

		call	SDiskReload	; reload system disk
		jc	short OpenRoot9	; error

; ------------- clear FILE descriptor

		call	ClearFileDesc	; clear FILE descriptor

; ------------- build FILE descriptor

		mov	byte [di+FILE_Name],PATHCHAR ; mark root directory
		shl	al,1
		shl	al,1
		shl	al,1
		shl	al,1		; AL <- shift disk to bit 4 position
		mov	byte [di+FILE_DirInx],al ; set device type and index
		mov	byte [di+FILE_Attr],DIR_DIR ; set directory attribute
		mov	ah,[bx+SDD_RootSect] ; AH <- number of root sectors
		mov	al,0
		shl	ax,1		; AX <- root size
		mov	[di+FILE_Size],ax ; root directory size

; ------------- pop registers

OpenRoot9:	pop	bx		; pop BX
		pop	ax		; pop AX		
		ret

; -----------------------------------------------------------------------------
;           Search files internal - prepare destination FILE structure
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT filename (with path)
;		SI = new offset in TEXT
;		DI = array of FILE structures (NULL = only get number of files)
;		BP = max. number of files (0 = no limit)
; OUTPUT:	DI = new FILE structure
;		BP = 1 on last entry, or unchanged
; -----------------------------------------------------------------------------

FindFilesDI:	inc	si		; SI <- skip separator
		cmp	si,[bx+TEXT_Length] ; check end of text
		jb	short FindFilesDI6 ; it is not last entry
		mov	bp,1		; BP <- found 1 entry
		or	di,di		; is destination buffer valid?
		jnz	short FindFilesDI8 ; buffer is valid
FindFilesDI6:	mov	di,FindFileFILE	; DI <- temporary FILE structure
FindFilesDI8:	dec	si		; return SI value
		ret

; -----------------------------------------------------------------------------
;                 Search files internal - open device
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT filename (with path)
;		CX = old offset in TEXT
;		SI = new offset in TEXT
;		DI = array of FILE structures (NULL = only get number of files)
;		BP = max. number of files (0 = no limit)
; OUTPUT:	CY = error, file not found or some error
;		BP = 1 on last entry, or unchanged
; -----------------------------------------------------------------------------

; ------------- push registers

FindFilesODev:	push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- prepare destination FILE structure -> DI

		call	FindFilesDI	; prepare FILE pointer -> DI

; ------------- open device

		xchg	si,cx		; SI <- old offset, CX <- new offset
		sub	cx,si		; CX <- length
		lea	si,[bx+TEXT_Text+si] ; SI <- device name
		call	OpenDevice	; open device

; ------------- pop registers

		pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                Search files internal - open root directory
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT filename (with path)
;		SI = new offset in TEXT
;		DI = array of FILE structures (NULL = only get number of files)
;		BP = max. number of files (0 = no limit)
; OUTPUT:	CY = error, file not found or some error
;		BP = 1 on last entry, or unchanged
; DESTROYS:	AX
; -----------------------------------------------------------------------------

; ------------- push registers

FindFilesORoot:	push	di		; push DI

; ------------- prepare destination FILE structure -> DI

		call	FindFilesDI	; prepare FILE pointer -> DI

; ------------- get disk -> AL

		mov	al,[FindFileFILE+FILE_DirInx] ; AL <- disk
		shr	al,1
		shr	al,1
		shr	al,1
		shr	al,1		; AL <- system disk

; ------------- open root directory

		call	OpenRoot	; open root directory

; ------------- pop registers

		pop	di		; pop DI
		ret

; -----------------------------------------------------------------------------
;                  Search files internal - open file
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT filename (with path)
;		CX = old offset in TEXT
;		DL = required attributes (incl. DIR_THISDIR and DIR_UPDIR)
;		DH = mask of attributes (incl. DIR_THISDIR and DIR_UPDIR)
;		SI = new offset in TEXT
;		DI = array of FILE structures (NULL = only get number of files)
;		BP = max. number of files (0 = no limit)
; OUTPUT:	CY = error, file not found or some error
;		BP = number of files on last entry, or 0 on error, or unchanged
; DESTROYS:	AX
; -----------------------------------------------------------------------------

; ------------- push registers

FindFilesOFile:	push	bx		; push BX
		push	cx		; push CX
		push	si		; push SI

; ------------- prepare to check end of text -> AX

		mov	ax,si		; AX <- new part
		inc	ax		; skip separator
		sub	ax,[bx+TEXT_Length] ; check end of text

; ------------- prepare text -> SI and length -> CX

		xchg	si,cx		; SI <- old offset, CX <- new offset
		sub	cx,si		; CX <- length
		lea	si,[bx+TEXT_Text+si] ; SI <- file name

; ------------- get disk descriptor -> BX

		push	di		; push DI
		mov	di,FindFileFILE	; DI <- temporary FILE structure
		call	FileSDD		; get disk descriptor -> SDD
		pop	di		; pop DI
		jc	short FindFilesOFile4 ; invalid disk

; ------------- check end of text

		or	ax,ax		; end of text?
		mov	ax,[FindFileFILE+FILE_Cluster]; AX <- directory cluster
		jns	short FindFilesOFile6 ; end of text, last entry

; ------------- find path entry

		push	dx		; push DX
		push	bp		; push BP
		push	di		; push DI

		mov	di,FindFileFILE	; DI <- FILE temporary
		mov	bp,1		; BP <- load 1 entry
		mov	dx,DIR_DIR*256+DIR_DIR ; required attributes
		call	FindDirEntry	; find directory entry

		pop	di		; pop DI
		pop	bp		; pop BP
		pop	dx		; pop DX
		jnc	short FindFilesOFile8

; ------------- error

FindFilesOFile4:xor	bp,bp		; BP <- 0, no files found on error
		stc			; set error flag
		jmp	short FindFilesOFile8

; ------------- find last entry (files)

FindFilesOFile6:call	FindDirEntry	; find directory entry

; ------------- pop registers

FindFilesOFile8:pop	si		; pop SI
		pop	cx		; pop CX
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                        Search files or directories
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT filename (with path)
;		DL = required attributes (incl. DIR_THISDIR and DIR_UPDIR)
;		DH = mask of attributes (incl. DIR_THISDIR and DIR_UPDIR)
;		DI = array of FILE structures (NULL = only get number of files)
;		BP = max. number of files (0 = no limit)
; OUTPUT:	CY = error, file not found or some error
;		BP = number of files
; NOTES:	Deleted entry is found only if mask starts with FATDELCHAR.
; -----------------------------------------------------------------------------

; ------------- push registers

FindFiles:	push	ax		; push AX
		push	cx		; push CX
		push	si		; push SI

; ------------- copy current path FILE structure

		push	di		; push DI
		mov	si,CurPathFILE	; SI <- current path FILE
		mov	cx,FILE_size/2	; size of FILE structure
		mov	di,FindFileFILE	; DI <- temporary FILE structure
		rep	movsw		; copy FILE structrure
		pop	di		; pop DI

; ------------- end of text

		xor	si,si		; SI <- offset in text
		cmp	si,[bx+TEXT_Length] ; end of text?
		je	short FindFiles94 ; end of text

; ------------- find next path separator ":", "/" or "\"

FindFiles1:	push	dx		; push DX
		mov	ax,"\/"		; separator 1 and 2
		mov	dl,":"		; separator 3
		mov	cx,si		; CX <- push old offset in text
		call	TextFind3ListNext ; find next separator
		pop	dx		; pop DX
		jnc	short FindFiles2 ; separator has been found
		mov	si,[bx+TEXT_Length] ; SI <- limit to text end
		jmp	short FindFiles3

; ------------- device

FindFiles2:	cmp	byte [bx+TEXT_Text+si],":" ; device?
		jne	short FindFiles3 ; not device
		call	FindFilesODev	; open device
		jnc	short FindFiles8 ; next part of filename

FindFiles94:	xor	bp,bp		; BP <- 0, no files found on error
		stc			; set error flag
		jmp	short FindFiles9 ; error

; ------------- path is not allowed on device other than system disk

FindFiles3:	test	byte [FindFileFILE+FILE_Attr],DIR_Device ; device?
		jz	short FindFiles4 ; not device
		test	byte [FindFileFILE+FILE_DirInx],FILEDEV_MASKTYPE ; SYS?
		jnz	short FindFiles94 ; not system disk

; ------------- auto open root directory on new system disk

		cmp	cx,si		; empty name?
		je	short FindFiles42 ; empty name, this is root
		push	bp		; push BP
		push	si		; push SI
		mov	si,cx		; don't act as last entry
		call	FindFilesORoot	; open root directory
		pop	si		; pop SI
		pop	bp		; pop BP
		jc	short FindFiles94 ; error

; ------------- empty name - root directory

FindFiles4:	cmp	cx,si		; empty text?
		jne	short FindFiles5 ; no

FindFiles42:	call	FindFilesORoot	; open root directory
		jc	short FindFiles94 ; error
		jmp	short FindFiles8 ; next part of filename

; ------------- find directory entry

FindFiles5:	test	byte [FindFileFILE+FILE_Attr],DIR_DIR ; directory?
		jz	short FindFiles94 ; error, not directory

		call	FindFilesOFile	; find file or dir entry
		jc	short FindFiles9 ; error, not found

; ------------- next part of filename

FindFiles8:	mov	cx,si		; CX <- next part
		inc	cx		; CX <- skip separator
		mov	si,cx		; SI <- next part
		cmp	cx,[bx+TEXT_Length] ; check end of text
		jb	short FindFiles1 ; analyte next part of path

; ------------- pop registers

FindFiles9:	pop	si		; pop SI
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                                  Open file
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT file name (with path)
;		DI = pointer to destination FILE structure
; OUTPUT:	CY = error, file not found
; -----------------------------------------------------------------------------

OpenFile:	push	dx		; push DX
		mov	dx,DIR_DIR*256	; attributes (no directory)
		jmp	short OpenDir2

; -----------------------------------------------------------------------------
;                               Open directory
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT directory name (with path)
;		DI = pointer to destination FILE structure
; OUTPUT:	CY = error, directory not found
; -----------------------------------------------------------------------------

OpenDir:	push	dx		; push DX
		mov	dx,(DIR_DIR+DIR_THISDIR+DIR_UPDIR)*256+DIR_DIR ; attr.
OpenDir2:	push	bp		; push BP
		mov	bp,1		; 1 file entry
		call	FindFiles	; open file or directory
		pop	bp		; pop BP
		pop	dx		; pop DX
		ret

; -----------------------------------------------------------------------------
;                        Check if file or directory exists
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT directory name (with path)
; OUTPUT:	CY = error, file not found
;		AX = number of files
; -----------------------------------------------------------------------------

FileExist:	push	dx		; push DX
		push	di		; push DI
		xchg	ax,bp		; AX <- push BP

		mov	dx,(DIR_THISDIR+DIR_UPDIR)*256 ; attributes
		xor	di,di		; DI <- 0, no FILE structure
		xor	bp,bp		; BP <- 0, no limit
		call	FindFiles	; open file or directory

		xchg	ax,bp		; pop BP, AX <- number of files
		pop	di		; pop DI
		pop	dx		; pop DX
		ret

; -----------------------------------------------------------------------------
;                      Get file attributes from FILE structure
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure
; OUTPUT:	AL = file attributes (including DIR_THISDIR and DIR_UPDIR)
; -----------------------------------------------------------------------------

FileAttr:	mov	al,[di+FILE_Attr] ; AL <- attributes
		and	al,DIR_Mask	; mask attributes
		cmp	word [di+FILE_Name],". " ; this directory?
		jne	short FileAttr2	; no
		or	al,DIR_THISDIR	; set "this directory" flag
FileAttr2:	cmp	word [di+FILE_Name],".." ; up directory?
		jne	short FileAttr4	; no
		or	al,DIR_UPDIR	; set "up directory" flag
FileAttr4:	ret

; -----------------------------------------------------------------------------
;                            Get file attributes
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT file or directory name (with path)
; OUTPUT:	CY = error, file not found (AL = 0)
;		AL = file attributes (incl.DIR_THISDIR and DIR_UPDIR,0 on err.)
; -----------------------------------------------------------------------------

; ------------- push registers

GetFileAttr:	push	dx		; push DX
		push	di		; push DI
		push	bp		; push BP

; ------------- find file

		xor	dx,dx		; DX <- attributes, any file
		mov	di,FindFileFILE	; DI <- temporary FILE structure
		mov	bp,1		; 1 file entry
		call	FindFiles	; open file or directory

; ------------- error

		mov	al,0		; AL <- 0 on error
		jc	short GetFileAttr4 ; error

; ------------- get attributes

		call	FileAttr	; get file attributes
		clc			; clear error flag

; ------------- pop registers

GetFileAttr4:	pop	bp		; pop BP
		pop	di		; pop DI
		pop	dx		; pop DX
		ret

; -----------------------------------------------------------------------------
;                            Set file attributes
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT file or directory name (with path)
;		AL = new file attributes (cannot change DIR attribute)
; OUTPUT:	CY = error
; -----------------------------------------------------------------------------

; ------------- push registers

SetFileAttr:	push	ax		; push AX
		push	dx		; push DX
		push	di		; push DI
		push	bp		; push BP

; ------------- find file

		xor	dx,dx		; DX <- attributes, any file
		mov	di,FindFileFILE	; DI <- temporary FILE structure
		mov	bp,1		; 1 file entry
		call	FindFiles	; open file or directory
		jc	short SetFileAttr4 ; error

; ------------- check attributes

		mov	ah,[di+FILE_Attr] ; AH <- old file attributes
		test	ah,DIR_Device
		stc			; set error flag
		jnz	short SetFileAttr4 ; invalid attributes
		xor	ah,al
		test	ah,DIR_DIR	; check DIR attribute
		stc			; set error flag
		jnz	short SetFileAttr4 ; cannot change DIR flag

; ------------- set new attributes

		and	al,DIR_Mask	; mask valid attributes
		or	al,DIR_Dirty	; set dirty flag
		mov	[di+FILE_Attr],al ; set new attributes
		call	FlushFile	; write changes

; ------------- pop registers

SetFileAttr4:	pop	bp		; pop BP
		pop	di		; pop DI
		pop	dx		; pop DX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                      Get file size from FILE structure
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure
; OUTPUT:	DX:AX = file size
; NOTES:	Does not modify flags.
; -----------------------------------------------------------------------------

FileSize:	mov	ax,[di+FILE_Size] ; AX <- file size LOW
		mov	dx,[di+FILE_Size+2] ; DX <- file size HIGH
		ret

; -----------------------------------------------------------------------------
;                            Get file size
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT file or directory name (with path)
; OUTPUT:	CY = error, file not found (DX:AX = 0)
;		DX:AX = file size (0 on error)
; -----------------------------------------------------------------------------

; ------------- push registers

GetFileSize:	push	di		; push DI
		push	bp		; push BP

; ------------- find file

		xor	ax,ax		; AX <- 0
		xor	dx,dx		; DX <- attributes, any file
		mov	di,FindFileFILE	; DI <- temporary FILE structure
		mov	bp,1		; 1 file entry
		call	FindFiles	; open file or directory
		jc	short GetFileSize4 ; error

; ------------- get size

		call	FileSize	; get file size -> DX:AX

; ------------- pop registers

GetFileSize4:	pop	bp		; pop BP
		pop	di		; pop DI
		ret

; -----------------------------------------------------------------------------
;                     Modify file (set current date and time)
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure
; -----------------------------------------------------------------------------

; ------------- push registers

ModifyFile:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- directory does not change date and time

		test	byte [di+FILE_Attr],DIR_DIR ; directory?
		jnz	short ModifyFile8 ; directory

; ------------- current date

		call	GetDate		; get current date
		call	PackDOSDate	; pack date to DOS file format
		mov	[di+FILE_Date],ax ; set current date

; ------------- current time

		call	GetTime		; get current time
		call	PackDOSTime	; pack time to DOS file format
		mov	[di+FILE_Time],ax ; set current time

; ------------- set dirty flag

ModifyFile8:	or	byte [di+FILE_Attr],DIR_Dirty ; set dirty flag

; ------------- pop registers

		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                       Set file (or directory) size
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure
;		DX:AX = new file size
; OUTPUT:	CY = error
; NOTES:	Does not initialize content of new clusters.
;		Directory size only growths, not decreases.
; -----------------------------------------------------------------------------

; ------------- push registers

SetFileSize:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- modify file

		call	ModifyFile	; modify file

; ------------- update FILE entry

		test	byte [di+FILE_Attr],DIR_DIR ; directory?
		jnz	short SetFileSize2 ; directory does not use size

		cmp	dx,[di+FILE_Size+2] ; check file size HIGH
		jne	short SetFileSize1 ; need update
		cmp	ax,[di+FILE_Size] ; check file size LOW
		jne	short SetFileSize1 ; need update
SetFileSize92:	jmp	SetFileSize9	; file size is OK

SetFileSize1:	mov	[di+FILE_Size],ax ; set new size LOW
		mov	[di+FILE_Size+2],dx ; set new size HIGH
		
; ------------- get system disk descriptor -> BX

SetFileSize2:	call	FileSDD		; get disk descriptor -> BX
		jc	short SetFileSize92 ; error

; ------------- convert file size to sectors -> DX:AX

		add	ax,SECTSIZE-1	; round size up
		adc	dx,byte 0	; carry
		rcr	dx,1
		rcr	ax,1		; DX:AX <- size / 2
		mov	al,ah
		mov	ah,dl
		mov	dl,dh
		mov	dh,0		; DX:AX <- number of sectors

; ------------- get new number of clusters -> CX

		mov	cl,[bx+SDD_SectClust] ; CL <- sectors per cluster
		call	GetClustNum	; get number of clusters
		jc	short SetFileSize92 ; error
		xchg	ax,cx		; CX <- number of clusters

; ------------- prepare end of chain flag -> SI

		mov	si,FAT12_RESMIN	; SI <- FAT12 invalid cluster
		test	byte [bx+SDD_FlagsDisk],SDDF_FAT16 ; FAT16 ?
		jz	short SetFileSize3 ; no
		mov	si,FAT16_RESMIN	; SI <- FAT16 invalid cluster

; ------------- find last valid cluster -> DX,AX

SetFileSize3:	xor	dx,dx		; DX <- 0, index of current cluster
		mov	ax,[di+FILE_Cluster] ; AX <- first cluster
		jcxz	SetFileSize34	; new end of chain reached

SetFileSize32:	cmp	ax,si		; old end of chain?
		jae	short SetFileSize5 ; old end of chain reached
		mov	dx,ax		; DX <- new current cluster
		call	GetNextClust	; get next cluster -> AX
		jc	short SetFileSize92 ; error
		loop	SetFileSize32	; next cluster

; ------------- mark end of file

		cmp	ax,si		; end of chain reached?
		jae	short SetFileSize9 ; end of chain has been found
		test	byte [di+FILE_Attr],DIR_DIR ; directory?
		jnz	short SetFileSize9 ; directory does not decrease
		xchg	ax,cx		; CX <- next cluster
		mov	ax,FAT16_END	; AX <- end mark
		jmp	short SetFileSize44

SetFileSize34:	test	byte [di+FILE_Attr],DIR_DIR ; directory?
		jnz	short SetFileSize9 ; directory does not decrease
		mov	[di+FILE_Cluster],si ; chain end
		add	word [di+FILE_Cluster],byte FAT16_END-FAT16_RESMIN
		or	word [di+FILE_ClustDisk],byte NOCLUST ; invalidate

; ------------- shorten chain

SetFileSize4:	cmp	ax,si		; end of chain reached?
		jae	short SetFileSize8 ; end of chain has been found
		cmp	ax,FAT16_MIN	; check cluster
		jb	short SetFileSize8 ; disk error
		mov	dx,ax		; DX <- new current cluster
		call	GetNextClust	; get next cluster -> AX
		jc	short SetFileSize8 ; error
		xchg	ax,cx		; CX <- next cluster
		xor	ax,ax		; AX <- free mark
SetFileSize44:	call	SetNextClust	; set next cluster (mark it free)
		jc	short SetFileSize8 ; error
		xchg	ax,cx		; AX <- next cluster
		jmp	short SetFileSize4 ; next cluster

; ------------- extend chain

SetFileSize5:	or	dx,dx		; valid cluster?
		jz	short SetFileSize54 ; no valid cluster
SetFileSize52:	mov	[LastFreeClust],dx ; last free cluster
SetFileSize54:	call	FindFreeClust	; find next free cluster -> AX
		jc	short SetFileSize8 ; error
		or	dx,dx		; first cluster?
		jnz	short SetFileSize56 ; not first cluster
		mov	[di+FILE_Cluster],ax ; mark first cluster
		or	word [di+FILE_ClustDisk],byte NOCLUST ; invalidate
		jmp	short SetFileSize58
SetFileSize56:	call	SetNextClust	; set next cluster
		jc	short SetFileSize8 ; error
SetFileSize58:	xchg	ax,dx		; DX <- new current cluster
		mov	ax,FAT16_END	; AX <- end mark
		call	SetNextClust	; mark end cluster
		jc	short SetFileSize8
		loop	SetFileSize5	; next cluster

; ------------- flush FAT sectors (CY = error)

SetFileSize8:	lahf			; AH <- flags
		call	FlushFAT	; flush FAT sectors
		mov	al,0		; AL <- 0
		adc	al,al		; AL <- error flag
		or	ah,al		; set carry flag
		sahf			; flags <- AH

; ------------- pop registers

SetFileSize9:	pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                    Get file date and time from FILE structure
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure
; OUTPUT:	AL = second (0..58, can be 0..62, only even second)
;		CL = minute (0..59, can be 0..63)
;		CH = hour (0..23, can be 0..31)
; 		DL = day (1..31, can be 0..31)
;		DH = month (1..12, can be 0..15)
;		SI = year (1980..2117)
; -----------------------------------------------------------------------------

; ------------- push registers

FileDateTime:	push	bx		; push BX
		push	ax		; push AX

; ------------- unpack date -> DL=day, DH=month, SI=year

		mov	ax,[di+FILE_Date] ; AX <- DOS date
		call	UnpackDOSDate	; unpack date

; ------------- unpack time -> BH=second, CL=minute, CH=hour

		mov	ax,[di+FILE_Time] ; AX <- DOS time
		call	UnpackDOSTime	; unpack time

; ------------- pop registers

		pop	ax		; pop AX
		mov	al,bh		; AL <- second
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                         Get file date and time
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT file or directory name (with path)
; OUTPUT:	CY = error, file not found (all entries = 0)
; 		AL = second (0..58, can be 0..62, only even second)
;		CL = minute (0..59, can be 0..63)
;		CH = hour (0..23, can be 0..31)
; 		DL = day (1..31, can be 0..31)
;		DH = month (1..12, can be 0..15)
;		SI = year (1980..2117)
; -----------------------------------------------------------------------------

; ------------- push registers

GetFileDateTime:push	di		; push DI
		push	bp		; push BP

; ------------- find file

		mov	al,0		; AL <- 0
		xor	cx,cx		; CX <- 0
		xor	dx,dx		; DX <- attributes, any file
		xor	si,si		; SI <- 0
		mov	di,FindFileFILE	; DI <- temporary FILE structure
		mov	bp,1		; 1 file entry
		call	FindFiles	; open file or directory
		jc	short GetFileDateTim4 ; error

; ------------- get date and time

		call	FileDateTime	; get file date and time
		clc			; clear error flag

; ------------- pop registers

GetFileDateTim4:pop	bp		; pop BP
		pop	di		; pop DI
		ret

; -----------------------------------------------------------------------------
;                         Set file date and time
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT file or directory name (with path)
; 		AL = second (0..58, can be 0..62, only even second)
;		CL = minute (0..59, can be 0..63)
;		CH = hour (0..23, can be 0..31)
; 		DL = day (1..31, can be 0..31)
;		DH = month (1..12, can be 0..15)
;		SI = year (1980..2117)
; OUTPUT:	CY = error
; -----------------------------------------------------------------------------

; ------------- push registers

SetFileDateTime:push	ax		; push AX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI
		push	bp		; push BP

; ------------- prepare DOS date -> AX and time -> CX

		push	bx		; push BX
		mov	bh,al		; BH <- second
		call	PackDOSTime	; pack time
		pop	bx		; pop BX
		xchg	ax,cx		; CX <- time
		call	PackDOSDate	; pack date

; ------------- find file

		xor	dx,dx		; DX <- attributes, any file
		mov	di,FindFileFILE	; DI <- temporary FILE structure
		mov	bp,1		; 1 file entry
		call	FindFiles	; open file or directory
		jc	short SetFileDateTim4 ; error

; ------------- set new date and time

		mov	[di+FILE_Date],ax ; set date
		mov	[di+FILE_Time],cx ; set time
		or	byte [di+FILE_Attr],DIR_Dirty ; set dirty flag
		call	FlushFile	; write changes

; ------------- pop registers

SetFileDateTim4:pop	bp		; pop BP
		pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                     Add filename from FILE structure
; -----------------------------------------------------------------------------
; INPUT:	SI = pointer to FILE structure
;		BX = destination TEXT (filename will be added after old text)
; OUTPUT:	BX = old or new pointer to TEXT structure
;		CY = memory error
; -----------------------------------------------------------------------------

; ------------- push registers

AddFileName:	push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI
		push	bp		; push BP

; ------------- get length of extension -> DX

		push	si		; push SI
		mov	dx,3		; DX <- max. length of extension
		add	si,11-1		; SI <- end of extension
AddFileName2:	cmp	byte [si]," "	; blank character?
		ja	short AddFileName3 ; found valid character
		dec	si		; SI <- shift text pointer
		dec	dx		; decrease length of extension
		jnz	short AddFileName2 ; check next character
AddFileName3:	pop	si		; pop SI

; ------------- get length of name -> BP

		push	si		; push SI
		mov	bp,8		; DX <- max. length of name
		add	si,8-1		; SI <- end of name
AddFileName4:	cmp	byte [si]," "	; blank character?
		ja	short AddFileName5 ; found valid character
		dec	si		; SI <- shift text pointer
		dec	bp		; decrease length of name
		jnz	short AddFileName4 ; check next character
AddFileName5:	pop	si		; pop SI

; ------------- resize data buffer

		mov	cx,[bx+TEXT_Length] ; CX <- current length of text
		mov	di,cx		; DI <- old length of text
		add	cx,bp		; add length of name
		cmp	dl,1		; check extension
		cmc			; CY = some extension
		adc	cx,dx		; add length of extension (and ".")
		call	TextResize	; resize text string
		jc	short AddFileName8 ; memory error

; ------------- copy name (here is NC)

		lea	di,[bx+TEXT_Text+di] ; DI <- destination address
		mov	cx,bp		; CX <- length of name
		push	si		; push SI
		rep	movsb		; copy name
		pop	si		; pop SI

; ------------- copy extension (here is NC)

		mov	cx,dx		; CX <- length of extension
		jcxz	AddFileName8	; no extension
		mov	byte [di],"."	; store extension separator
		inc	di		; DI <- increase destination pointer
		lea	si,[si+8]	; SI <- start of extension
		rep	movsb		; copy extension

; ------------- pop registers

AddFileName8:	pop	bp		; pop BP
		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		ret

; -----------------------------------------------------------------------------
;                       Get file seek position
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure
; OUTPUT:	DX:AX = offset in file (0 on error)
;		CY = invalid disk (DX:AX = 0)
; -----------------------------------------------------------------------------

; ------------- push registers

FilePos:	push	bx		; push BX

; ------------- offset in device or root

		mov	dx,[di+FILE_ClustOff+2] ; DX <- offset HIGH
		mov	ax,[di+FILE_ClustOff] ; AX <- offset LOW
		test	byte [di+FILE_Attr],DIR_Device ; device?
		jnz	short FilePos4	; device
		cmp	byte [di+FILE_Name],PATHCHAR ; root?
		je	short FilePos4	; root

; ------------- get system disk descriptor -> BX

		xor	ax,ax		; AX <- 0
		xor	dx,dx		; DX <- 0
		call	FileSDD		; get disk descriptor -> BX
		jc	short FilePos4	; error

; ------------- get current position -> DX:AX

		mov	ax,[di+FILE_ClustFile] ; AX <- cluster in file
		mul	word [bx+SDD_ClustSize] ; DX:AX <- offset
		add	ax,[di+FILE_ClustOff] ; add offset in cluster
		adc	dx,byte 0	; carry
		clc			; clear error flag

; ------------- pop registers

FilePos4:	pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                       Set file seek position
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = offset in file or in device (0 on error)
;		DI = pointer to FILE structure
;		CL = seek mode (SEEK_BEG, SEEK_CUR, SEEK_END)
; OUTPUT:	CY = invalid disk
; NOTES:	Can seek behind end of file.
; -----------------------------------------------------------------------------

; ------------- push registers

FileSeek:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- seek from begin

		cmp	cl,SEEK_BEG	; seek from begin?
		je	short FileSeek3	; seek from begin

; ------------- seek from end

		cmp	cl,SEEK_END	; seek from end?
		jne	short FileSeek2	; no
		add	ax,[di+FILE_Size] ; AX <- offset from end LOW
		adc	dx,[di+FILE_Size+2] ; DX <- offset from end HIGH
		jmp	short FileSeek3

; ------------- seek from current position

FileSeek2:	xchg	ax,cx		; CX <- offset LOW
		xchg	dx,bx		; BX <- offset HIGH
		call	FilePos		; get current position -> DX:AX
		jc	short FileSeek9	; error
		add	ax,cx		; AX <- offset LOW
		adc	dx,bx		; DX <- offset HIGH

; ------------- seek device or root

FileSeek3:	test	byte [di+FILE_Attr],DIR_Device ; device?
		jnz	short FileSeek4	; device
		cmp	byte [di+FILE_Name],PATHCHAR ; root?
		jne	short FileSeek5	; not root
FileSeek4:	mov	[di+FILE_Off],ax ; set new offset LOW
		mov	[di+FILE_Off+2],dx ; set new offset HIGH
		jmp	short FileSeek9

; ------------- get system disk descriptor -> BX

FileSeek5:	call	FileSDD		; get disk descriptor -> BX
		jc	short FileSeek9	; error

; ------------- recalculate offset to cluster

		mov	si,[bx+SDD_ClustSize] ; SI <- cluster size
		xchg	ax,cx		; CX <- offset LOW
		xchg	ax,dx		; AX <- offset HIGH
		xor	dx,dx		; DX <- 0
		div	si		; AX <- cluster HIGH, DX <- offset
		xchg	ax,cx		; CX <- cluster HIGH, AX <- offset LOW
		div	si		; CX:AX <- cluster, DX <- offset
		jcxz	FileSeek6	; no overflow
		or	ax,byte -1	; AX <- limit cluster
		mov	dx,si		; DX <- cluster size
		dec	dx		; DX <- limit offset
FileSeek6:	mov	[di+FILE_ClustOff],dx ; set new offset in cluster

; ------------- invalidate disk cluster

		mov	dx,[di+FILE_ClustFile] ; DX <- current cluster
		cmp	ax,dx		; cluster changed?
		je	short FileSeek9	; cluster not changed
		mov	[di+FILE_ClustFile],ax ; set new cluster
		inc	dx		; DX <- next cluster
		cmp	ax,dx		; cluster increased?
		jne	short FileSeek8	; no

		mov	ax,[di+FILE_ClustDisk] ; AX <- current cluster on disk
		inc	ax		; invalid cluster?
		jz	short FileSeek9 ; invalid cluster
		dec	ax		; AX <- current cluster
		call	GetNextClust	; get next cluster
		jc	short FileSeek8	; error
		mov	[di+FILE_ClustDisk],ax ; new cluster
		jmp	short FileSeek9

FileSeek8:	or	word [di+FILE_ClustDisk],byte NOCLUST ; invalidate

; ------------- pop registers

FileSeek9:	pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                     Set file seek position from begin
; -----------------------------------------------------------------------------
; INPUT:	DX:AX = offset in file or in device from begin (0 on error)
;		DI = pointer to FILE structure
; OUTPUT:	CY = invalid disk
; NOTES:	Can seek behind end of file.
; -----------------------------------------------------------------------------

FileSetPos:	push	cx		; push CX
		mov	cl,SEEK_BEG	; seek mode
		call	FileSeek	; set file pointer
		pop	cx		; pop CX
		ret		

; -----------------------------------------------------------------------------
;                       Reset file position to begin
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure
; OUTPUT:	CY = invalid disk
; -----------------------------------------------------------------------------

; ------------- push registers

FileReset:	push	ax		; push AX
		push	dx		; push DX

; ------------- reset file pointer

		xor	ax,ax		; AX <- 0
		xor	dx,dx		; DX <- 0
		call	FileSetPos	; set file pointer

; ------------- pop registers

		pop	dx		; pop DX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                       Get current path
; -----------------------------------------------------------------------------
; OUTPUT:	BX = pointer to CTEXT with current path
; -----------------------------------------------------------------------------

GetCurPath:	mov	bx,CurPath	; BX <- current path
		ret

; -----------------------------------------------------------------------------
;                       Set current path
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT with new current path
; OUTPUT:	CY = error
; -----------------------------------------------------------------------------

; ------------- push registers

SetCurPath:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	si		; push SI
		push	di		; push DI

; ------------- absolutize path

		call	AbsPath		; absolutize path
		jc	short SetCurPath8 ; error
		xchg	ax,bx		; BX <- new TEXT

; ------------- check path

		cmp	word [bx+TEXT_Length],byte 3 ; minimal length
		jb	short SetCurPath7 ; invalid path

		mov	ax,[bx+TEXT_Text] ; AX <- first 2 characters
		cmp	ah,":"		; 1-leter disk?
		jne	short SetCurPath7 ; invalid device

		sub	al,"A"		; AL <- system disk
		push	bx		; push BX
		call	SDiskGetDesc	; get SDD descriptor
		pop	bx		; pop BX
		jc	short SetCurPath7 ; invalid disk

		mov	ax,"?*"		; invalid characters
		call	TextFind2ListFirst ; check characters
		jnc	short SetCurPath7 ; chars not allowed

; ------------- open current directory

		mov	di,CurPathFILE	; DI <- current directory FILE
		call	OpenDir		; open directory
		jnc	short SetCurPath2 ; operation OK
		call	TextFree	; destroys temporary text
		stc			; set error flag
		jmp	short SetCurPath8

; ------------- limit path length

SetCurPath2:	mov	cx,[bx+TEXT_Length] ; CX <- length of current path
		cmp	cx,PATHMAX	; check path length
		jbe	SetCurPath4	; path length is OK
		mov	cx,PATHMAX	; limit path length

; ------------- copy new current path

SetCurPath4:	mov	di,CurPath+2	; DI <- current path
		mov	[di-TEXT_Text+TEXT_Length],cx ; set length of path
		lea	si,[bx+TEXT_Text] ; SI <- new path
		rep	movsb		; copy new path

; ------------- destroy temporary PATH

SetCurPath7:	call	TextFree	; destroy temporary text
		clc			; clear error flag

; ------------- pop registers

SetCurPath8:	pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                           Absolutize path
; -----------------------------------------------------------------------------
; INPUT:	BX = pointer to TEXT with relative path
; OUTPUT:	AX = new TEXT with absolute path (without ending "\", uppercase)
;		CY = error (AX = 0 on error)
; -----------------------------------------------------------------------------

; ------------- push registers

AbsPath:	push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI
		push	bp		; push BP
		mov	bp,bx		; BP <- input TEXT

; ------------- duplicate current path -> DI

		mov	bx,CurPath	; BX <- current path
		call	TextDup		; duplicate current path
		jnc	short AbsPath0	; OK
		jmp	AbsPath9	; memory error
AbsPath0:	xchg	ax,di		; DI <- output TEXT

; ------------- add path separator

		xor	si,si		; SI <- offset in input TEXT
AbsPath1:	mov	bx,di		; BX <- output TEXT
		call	TextAddPathSep	; add path separator
		mov	di,bx		; DI <- new output TEXT
		jc	short AbsPath82	; memory error

; ------------- find next path separator -> SI

		mov	bx,bp		; BX <- input TEXT
		mov	ax,"\/"		; separator 1 and 2
		mov	dl,":"		; separator 3
		mov	cx,si		; CX <- push old offset in text
		call	TextFind3ListNext ; find next separator
		jnc	short AbsPath2 ; separator has been found
		mov	si,[bx+TEXT_Length] ; SI <- limit to text end
		jmp	short AbsPath3

; ------------- device

AbsPath2:	cmp	byte [bx+TEXT_Text+si],":" ; device?
		jne	short AbsPath3 ; not device

		push	si		; push SI (new offset)
		and	word [di+TEXT_Length],byte 0 ; destroy path
		xchg	cx,si		; SI <- old offset, CX <- new offset
		sub	cx,si		; CX <- length of device name
		inc	cx		; CX <- including ":"
		lea	si,[bp+TEXT_Text+si] ; SI <- input text
		mov	bx,di		; BX <- output TEXT
		call	TextAddBuf	; add device
		mov	di,bx		; DI <- new output TEXT
		pop	si		; pop SI (new offset)
		jc	short AbsPath82	; memory error
		jmp	short AbsPath32	; add path separator

; ------------- root

AbsPath3:	cmp	si,cx		; empty segment?
		jne	short AbsPath4	; not root

		mov	bx,di		; BX <- output TEXT
		mov	al,":"		; AL <- separator
		xchg	si,cx		; CX <- new offset
		call	TextFindChFirst	; find ":" separator -> SI
		xchg	si,cx		; SI <- new offset, CX <- ":" offset
		jc	short AbsPath8	; internal error?
		inc	cx		; CX <- including ":"
		mov	[bx+TEXT_Length],cx ; left only device
AbsPath32:	call	TextAddPathSep	; add path separator
		mov	di,bx		; DI <- new output TEXT
AbsPath82:	jc	short AbsPath8	; memory error
		jmp	short AbsPath7	; next segment

; ------------- skip "." segment

AbsPath4:	mov	ax,si		; AX <- new offset
		sub	ax,cx		; AX <- segment length
		cmp	ax,byte 1	; 1 char?
		jne	short AbsPath5	; not 1 char
		xchg	si,cx		; SI <- old offset, CX <- new offset
		cmp	byte [bx+TEXT_Text+si],"." ; "." segment?
		xchg	si,cx		; SI <- new offset, CX <- old offset
		je	short AbsPath7	; skip this segment

; ------------- upper directory ".."

AbsPath5:	cmp	ax,byte 2	; 2 chars?
		jne	short AbsPath6	; not 2 chars
		xchg	si,cx		; SI <- old offset, CX <- new offset
		cmp	word [bx+TEXT_Text+si],".." ; ".." segment?
		xchg	si,cx		; SI <- new offset, CX <- old offset
		jne	short AbsPath6	; not ".." segment

		mov	bx,di		; BX <- output TEXT
		call	TextDelPathSep	; delete end path separator
		mov	al,PATHCHAR	; AL <- path separator
		xchg	si,cx		; CX <- new offset
		call	TextFindChLast	; find last path separator
		xchg	si,cx		; SI <- new offset, CX <- path sep.
		jc	short AbsPath7	; already root
		inc	cx		; CX <- including path separator
		mov	[bx+TEXT_Length],cx ; set new text length
		jmp	short AbsPath7	; next segment

; ------------- add segment

AbsPath6:	push	si		; push SI (new offset)
		mov	si,cx		; SI <- old offset
		lea	si,[bx+TEXT_Text+si] ; SI <- input text
		xchg	ax,cx		; CX <- segment length
		mov	bx,di		; BX <- output TEXT
		call	TextAddBuf	; add device
		mov	di,bx		; DI <- new output TEXT
		pop	si		; pop SI (new offset)
		jc	short AbsPath8	; memory error

; ------------- shift to next segment

AbsPath7:	inc	si		; SI <- skip path separator
		cmp	si,[ds:bp+TEXT_Length] ; end of text?
		jae	short AbsPath72	; end of text
		jmp	AbsPath1	; next segment

; ------------- convert to uppercase

AbsPath72:	mov	bx,di		; BX <- output TEXT
		call	TextUpper	; convert to uppercase letters

; ------------- delete last path separator

		call	TextDelPathSep	; delete end path separator
		xchg	ax,bx		; AX <- output TEXT
		clc			; clear error flag
		jmp	short AbsPath9

; ------------- memory error, destroy output TEXT

AbsPath8:	mov	bx,di		; BX <- output TEXT
		call	TextFree	; destroy text
		xor	ax,ax		; AX <- invalidate output TEXT
		stc			; set error flag

; ------------- pop registers

AbsPath9:	pop	bp		; pop BP
		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		ret

; -----------------------------------------------------------------------------
;                              Read from file
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure (must be open)
;		SI = destination buffer
;		CX = number of bytes
; OUTPUT:	CY = error
;		CX = successfully readed bytes
; -----------------------------------------------------------------------------
; Local variables:
%define RF_FILE		[bp+2]		; pointer to FILE structure
%define RF_READED	[bp+0]		; successfully readed bytes
%define RF_SIZEREM	[bp-2]		; remaining number of bytes
%define RF_BUFF		[bp-4]		; destination buffer
%define RF_CHSIZE	[bp-6]		; size of continuous chain
%define RF_CHOFFH	[bp-8]		; start offset of continuous chain HIGH
%define RF_CHOFFL	[bp-10]		; start offset of continuous chain LOW
%define RF_CHCLUST	[bp-12]		; last cluster of chain

; ------------- push registers

ReadFile:	push	ax		; push AX
		push	bx		; push BX
		push	dx		; push DX
		push	si		; push SI
		push	bp		; push BP
		push	di		; [bp+2] RF_FILE (-> DI)
		xor	ax,ax		; AX <- 0
		push	ax		; [bp+0] RF_READED (-> CX)
		mov	bp,sp		; BP <- local variables
		push	cx		; [bp-2] RF_SIZEREM
		push	si		; [bp-4] RF_BUFF
		push	ax		; [bp-6] RF_CHSIZE
		push	ax		; [bp-8] RF_CHOFFH
		push	ax		; [bp-10] RF_CHOFFL
		push	ax		; [bp-12] RF_CHCLUST

; ------------- read from device (here is AX = 0)

		xor	dx,dx		; DX <- 0
		mov	bl,[di+FILE_DirInx] ; BL <- device type and index
		test	byte [di+FILE_Attr],DIR_Device ; device?
		jnz	short ReadFile2	; read data from device

; ------------- read from root directory

		call	FileSDD		; get SDD descriptor -> BX
		jc	short ReadFile92 ; error, invalid disk number
		cmp	byte [di+FILE_Name],PATHCHAR ; root?
		jne	short ReadFile3	; not root

		mov	al,0		; AL <- 0
		mov	ah,[bx+SDD_RootStart] ; AX <- start sector LOW * 256
		mov	dx,[bx+SDD_RootStart+1] ; DX <- start sector HIGH
		sub	ah,[bx+SDD_StartSect] ; AX <- start sector relative LOW
		sbb	dx,[bx+SDD_StartSect+1] ; DX <- start sector HIGH
		shl	ax,1		; AX <- start offset LOW
		rcl	dx,1		; DX <- start offset HIGH

		mov	bl,[di+FILE_DirInx] ; BL <- disk and index of entry
		and	bl,FILEDEV_MASKINX ; mask index -> system disk type

ReadFile2:	add	ax,[di+FILE_Off] ; AX <- current offset LOW
		add	dx,[di+FILE_Off+2] ; DX <- current offset HIGH
		add	[di+FILE_Off],cx ; shift offset LOW
		adc	word [di+FILE_Off+2],byte 0 ; shift offset HIGH
		mov	di,si		; DI <- buffer
		mov	si,cx		; SI <- required size
		call	DevRead		; read from device

		pushf			; push flags
		sub	si,cx		; SI <- readed bytes
		mov	RF_READED,si	; set readed bytes
		popf			; pop flags

ReadFile92:	jmp	ReadFile9

; ------------- limit data size (to end of file)

ReadFile3:	test	byte [di+FILE_Attr],DIR_DIR ; directory?
		jnz	short ReadFile4 ; directory has unknown size

		call	FilePos		; get file seek position -> DX:AX
		mov	cx,[di+FILE_Size] ; CX <- file size LOW
		mov	si,[di+FILE_Size+2] ; SI <- file size HIGH
		sub	cx,ax		; CX <- remaining bytes LOW
		sbb	si,dx		; SI <- remaining bytes HIGH
		jnz	short ReadFile4 ; data size is OK
		cmp	cx,RF_SIZEREM	; check number of bytes
		jae	short ReadFile4 ; number of bytes is OK
		mov	RF_SIZEREM,cx	; limit number of bytes

; ------------- update start disk cluster

ReadFile4:	mov	ax,[di+FILE_ClustDisk] ; AX <- current disk cluster
		cmp	ax,byte NOCLUST ; valid cluster?
		jne	short ReadFile5 ; current cluster is valid

		mov	ax,[di+FILE_Cluster] ; AX <- start cluster
		mov	dx,[di+FILE_ClustFile] ; DX <- relative cluster
		or	dx,dx		; first cluster?
		jz	short ReadFile48 ; use first cluster
ReadFile42:	cmp	ax,byte NOCLUST	; end of file?
		je	short ReadFile48 ; end of file
		call	GetNextClust	; get next cluster
		jc	short ReadFile92 ; error
		dec	dx		; cluster counter
		jnz	ReadFile42	; get next cluster
ReadFile48:	mov	[di+FILE_ClustDisk],ax ; set new disk cluster

; ------------- pointer is behind end of file (here is AX = disk cluster)

ReadFile5:	cmp	ax,byte NOCLUST ; valid cluster?
		je	short ReadFile82 ; not valid cluster, end of data

; ------------- check continuous chain and flush chain

		mov	dx,RF_CHCLUST	; DX <- last cluster
		inc	dx		; DX <- next cluster
		cmp	ax,dx		; is chain continuous?
		je	short ReadFile6	; chain is continuous
		call	ReadFileChFlush	; load data of one chain
		jc	short ReadFile9	; error

; ------------- mark start of chain

ReadFile6:	cmp	word RF_CHSIZE,byte 0 ; any chain?
		jne	short ReadFile64 ; chain was already started

		sub	ax,byte 2	; AX <- relative cluster
		jc	short ReadFile9	; invalid cluster

		mov	RF_CHCLUST,ax	; chain cluster
		xchg	ax,dx		; DX <- cluster
		mov	al,[bx+SDD_SectClust] ; AL <- sectors per cluster
		cbw			; AX <- sectors per cluster
		mul	dx		; DX:AX <- relative sector
		add	ax,[bx+SDD_ClustStart] ; AX <- absolute sector LOW
		adc	dx,[bx+SDD_ClustStart+2] ; DX <- absolute sector HIGH
		sub	ax,[bx+SDD_StartSect] ; AX <- sector on system disk LOW
		sbb	dx,[bx+SDD_StartSect+2] ; DX <- sector on sys.disk HIGH
		mov	dh,dl
		mov	dl,ah
		mov	ah,al
		mov	al,0		; DX:AX <- sector * 256
		shl	ax,1
		rcl	dx,1		; DX:AX <- offset on system disk
		add	ax,[di+FILE_ClustOff] ; add offset in cluster
		adc	dx,byte 0	; carry
		mov	RF_CHOFFL,ax	; chain start LOW
		mov	RF_CHOFFH,dx	; chain start HIGH

; ------------- prepare size in this cluster

ReadFile64:	mov	ax,[bx+SDD_ClustSize] ; AX <- cluster size
		mov	dx,ax		; DX <- cluster size
		sub	ax,[di+FILE_ClustOff] ; AX <- remainder to end of cluster
		cmp	ax,RF_SIZEREM	; check remaining bytes
		jbe	short ReadFile65 ; data size is OK
		mov	ax,RF_SIZEREM	; AX <- limit data size
ReadFile65:	or	ax,ax		; any data left?
ReadFile82:	jz	short ReadFile8	; end of data
		add	RF_CHSIZE,ax	; increase chain size
		sub	RF_SIZEREM,ax	; decrease remaining data

; ------------- shift to next cluster

		add	[di+FILE_ClustOff],ax ; shift cluster offset
		cmp	dx,[di+FILE_ClustOff] ; is cluster full?
		ja	short ReadFile8	; end of data
		and	word [di+FILE_ClustOff],byte 0 ; clear cluster offset
		inc	word [di+FILE_ClustFile] ; increase cluster in file

		mov	ax,[di+FILE_ClustDisk] ; AX <- cluster on disk
		call	GetNextClust	; get next cluster
		jc	short ReadFile9 ; error
		mov	[di+FILE_ClustDisk],ax ; set new disk cluster
		jmp	ReadFile5

; ------------- flush last chain

ReadFile8:	call	ReadFileChFlush	; load data of one chain

; ------------- pop registers (CY=error)

ReadFile9:	mov	sp,bp		; SP <- destroy local variables
		pop	cx		; pop CX <- RF_READED
		pop	di		; pop DI
		pop	bp		; pop BP
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                     Read from file internal - flush chain
; -----------------------------------------------------------------------------
; INPUT:	BP = local variables
; OUTPUT:	CY = error
; DESTROYS:	CX, DX
; -----------------------------------------------------------------------------

ReadFileChFlush:push	ax		; push AX
		push	bx		; push BX
		push	di		; push DI

		mov	cx,RF_CHSIZE	; CX <- size of chain
		clc			; clear error flag
		jcxz	ReadFileChFlsh6	; chain is empty
		mov	ax,RF_CHOFFL	; AX <- start of chain LOW
		mov	dx,RF_CHOFFH	; DX <- start of chain HIGH
		mov	di,RF_FILE	; DI <- FILE structure
		mov	bl,[di+FILE_DirInx] ; BL <- disk and index of entry
		and	bl,FILEDEV_MASKINX ; mask index -> system disk type
		mov	di,RF_BUFF	; DI <- buffer
		call	DevRead		; read data

		pushf			; push flags
		mov	ax,RF_CHSIZE	; AX <- size of chain
		sub	ax,cx		; AX <- readed bytes
		add	RF_READED,ax	; increase readed counter
		add	RF_BUFF,ax	; shift buffer address
		and	word RF_CHSIZE,byte 0 ; clear size of chain
		popf			; pop flags

ReadFileChFlsh6:pop	di		; pop DI
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

%undef RF_FILE
%undef RF_READED
%undef RF_SIZEREM
%undef RF_BUFF
%undef RF_CHSIZE
%undef RF_CHOFFH
%undef RF_CHOFFL
%undef RF_CHCLUST

; -----------------------------------------------------------------------------
;                              Write to file
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure (must be open)
;		SI = source buffer
;		CX = number of bytes
; OUTPUT:	CY = error
;		CX = successfully written bytes
; -----------------------------------------------------------------------------
; Local variables:
%define WF_FILE		[bp+2]		; pointer to FILE structure
%define WF_WRITTEN	[bp+0]		; successfully written bytes
%define WF_SIZEREM	[bp-2]		; remaining number of bytes
%define WF_BUFF		[bp-4]		; source buffer
%define WF_CHSIZE	[bp-6]		; size of continuous chain
%define WF_CHOFFH	[bp-8]		; start offset of continuous chain HIGH
%define WF_CHOFFL	[bp-10]		; start offset of continuous chain LOW
%define WF_CHCLUST	[bp-12]		; last cluster of chain

; ------------- push registers

WriteFile:	push	ax		; push AX
		push	bx		; push BX
		push	dx		; push DX
		push	si		; push SI
		push	bp		; push BP
		push	di		; [bp+2] WF_FILE (-> DI)
		xor	ax,ax		; AX <- 0
		push	ax		; [bp+0] WF_WRITTEN (-> CX)
		mov	bp,sp		; BP <- local variables
		push	cx		; [bp-2] WF_SIZEREM
		push	si		; [bp-4] WF_BUFF
		push	ax		; [bp-6] WF_CHSIZE
		push	ax		; [bp-8] WF_CHOFFH
		push	ax		; [bp-10] WF_CHOFFL
		push	ax		; [bp-12] WF_CHCLUST

; ------------- check R/O attribute

		test	byte [di+FILE_Attr],DIR_RO ; check R/O attribute
		stc			; set error flag
		jnz	short WriteFile92 ; cannot save to file

; ------------- modify file

		call	ModifyFile	; modify file

; ------------- write to device (here is AX = 0)

		xor	dx,dx		; DX <- 0
		mov	bl,[di+FILE_DirInx] ; BL <- device type and index
		test	byte [di+FILE_Attr],DIR_Device ; device?
		jnz	short WriteFile2 ; write data to device

; ------------- write to root directory

		call	FileSDD		; get SDD descriptor -> BX
		jc	short WriteFile92 ; error, invalid disk number
		cmp	byte [di+FILE_Name],PATHCHAR ; root?
		jne	short WriteFile3 ; not root

		mov	al,0		; AL <- 0
		mov	ah,[bx+SDD_RootStart] ; AX <- start sector LOW * 256
		mov	dx,[bx+SDD_RootStart+1] ; DX <- start sector HIGH
		sub	ah,[bx+SDD_StartSect] ; AX <- start sector relative LOW
		sbb	dx,[bx+SDD_StartSect+1] ; DX <- start sector HIGH
		shl	ax,1		; AX <- start offset LOW
		rcl	dx,1		; DX <- start offset HIGH

		mov	bl,[di+FILE_DirInx] ; BL <- disk and index of entry
		and	bl,FILEDEV_MASKINX ; mask index -> system disk type

WriteFile2:	add	ax,[di+FILE_Off] ; AX <- current offset LOW
		add	dx,[di+FILE_Off+2] ; DX <- current offset HIGH
		add	[di+FILE_Off],cx ; shift offset LOW
		adc	word [di+FILE_Off+2],byte 0 ; shift offset HIGH
		mov	di,si		; DI <- buffer
		mov	si,cx		; SI <- required size
		call	DevWrite	; write to device

		pushf			; push flags
		sub	si,cx		; SI <- readed bytes
		mov	WF_WRITTEN,si	; set written bytes
		popf			; pop flags

WriteFile92:	jmp	WriteFile9

; ------------- increase file (or directory) size

WriteFile3:	call	FilePos		; get file seek position -> DX:AX
		add	ax,WF_SIZEREM	; add required size
		adc	dx,byte 0	; carry
		test	byte [di+FILE_Attr],DIR_DIR ; directory?
		jnz	short WriteFile36 ; directory has unknown size
		cmp	dx,[di+FILE_Size+2] ; check file size HIGH
		jne	short WriteFile34
		cmp	ax,[di+FILE_Size] ; check file size LOW
WriteFile34:	jbe	short WriteFile4 ; file size is OK
WriteFile36:	call	SetFileSize	; set file (directory) size
		jc	short WriteFile92 ; error

; ------------- update start disk cluster

WriteFile4:	mov	ax,[di+FILE_ClustDisk] ; AX <- current disk cluster
		cmp	ax,byte NOCLUST ; valid cluster?
		jne	short WriteFile5 ; current cluster is valid

		mov	ax,[di+FILE_Cluster] ; AX <- start cluster
		mov	dx,[di+FILE_ClustFile] ; DX <- relative cluster
		or	dx,dx		; first cluster?
		jz	short WriteFile48 ; use first cluster
WriteFile42:	cmp	ax,byte NOCLUST	; end of file?
		je	short WriteFile48 ; end of file
		call	GetNextClust	; get next cluster
		jc	short WriteFile92 ; error
		dec	dx		; cluster counter
		jnz	WriteFile42	; get next cluster
WriteFile48:	mov	[di+FILE_ClustDisk],ax ; set new disk cluster

; ------------- pointer is behind end of file (here is AX = disk cluster)

WriteFile5:	cmp	ax,byte NOCLUST ; valid cluster?
		je	short WriteFile82 ; not valid cluster, end of data

; ------------- check continuous chain and flush chain

		mov	dx,WF_CHCLUST	; DX <- last cluster
		inc	dx		; DX <- next cluster
		cmp	ax,dx		; is chain continuous?
		je	short WriteFile6 ; chain is continuous
		call	WriteFileChFlsh ; save data of one chain
		jc	short WriteFile9 ; error

; ------------- mark start of chain

WriteFile6:	cmp	word WF_CHSIZE,byte 0 ; any chain?
		jne	short WriteFile64 ; chain was already started

		sub	ax,byte 2	; AX <- relative cluster
		jc	short WriteFile9 ; invalid cluster

		mov	WF_CHCLUST,ax	; chain cluster
		xchg	ax,dx		; DX <- cluster
		mov	al,[bx+SDD_SectClust] ; AL <- sectors per cluster
		cbw			; AX <- sectors per cluster
		mul	dx		; DX:AX <- relative sector
		add	ax,[bx+SDD_ClustStart] ; AX <- absolute sector LOW
		adc	dx,[bx+SDD_ClustStart+2] ; DX <- absolute sector HIGH
		sub	ax,[bx+SDD_StartSect] ; AX <- sector on system disk LOW
		sbb	dx,[bx+SDD_StartSect+2] ; DX <- sector on sys.disk HIGH
		mov	dh,dl
		mov	dl,ah
		mov	ah,al
		mov	al,0		; DX:AX <- sector * 256
		shl	ax,1
		rcl	dx,1		; DX:AX <- offset on system disk
		add	ax,[di+FILE_ClustOff] ; add offset in cluster
		adc	dx,byte 0	; carry
		mov	WF_CHOFFL,ax	; chain start LOW
		mov	WF_CHOFFH,dx	; chain start HIGH

; ------------- prepare size in this cluster

WriteFile64:	mov	ax,[bx+SDD_ClustSize] ; AX <- cluster size
		mov	dx,ax		; DX <- cluster size
		sub	ax,[di+FILE_ClustOff] ; AX <- remainder to end of cluster
		cmp	ax,WF_SIZEREM	; check remaining bytes
		jbe	short WriteFile65 ; data size is OK
		mov	ax,WF_SIZEREM	; AX <- limit data size
WriteFile65:	or	ax,ax		; any data left?
WriteFile82:	jz	short WriteFile8 ; end of data
		add	WF_CHSIZE,ax	; increase chain size
		sub	WF_SIZEREM,ax	; decrease remaining data

; ------------- shift to next cluster

		add	[di+FILE_ClustOff],ax ; shift cluster offset
		cmp	dx,[di+FILE_ClustOff] ; is cluster full?
		ja	short WriteFile8	; end of data
		and	word [di+FILE_ClustOff],byte 0 ; clear cluster offset
		inc	word [di+FILE_ClustFile] ; increase cluster in file

		mov	ax,[di+FILE_ClustDisk] ; AX <- cluster on disk
		call	GetNextClust	; get next cluster
		jc	short WriteFile9 ; error
		mov	[di+FILE_ClustDisk],ax ; set new disk cluster
		jmp	WriteFile5

; ------------- flush last chain

WriteFile8:	call	WriteFileChFlsh ; load data of one chain

; ------------- pop registers (CY=error)

WriteFile9:	mov	sp,bp		; SP <- destroy local variables
		pop	cx		; pop CX <- WF_WRITTEN
		pop	di		; pop DI
		pop	bp		; pop BP
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                     Write to file internal - flush chain
; -----------------------------------------------------------------------------
; INPUT:	BP = local variables
; OUTPUT:	CY = error
; DESTROYS:	CX, DX
; -----------------------------------------------------------------------------

WriteFileChFlsh:push	ax		; push AX
		push	bx		; push BX
		push	di		; push DI

		mov	cx,WF_CHSIZE	; CX <- size of chain
		clc			; clear error flag
		jcxz	WriteFileChFls6 ; chain is empty
		mov	ax,WF_CHOFFL	; AX <- start of chain LOW
		mov	dx,WF_CHOFFH	; DX <- start of chain HIGH
		mov	di,WF_FILE	; DI <- FILE structure
		mov	bl,[di+FILE_DirInx] ; BL <- disk and index of entry
		and	bl,FILEDEV_MASKINX ; mask index -> system disk type
		mov	di,WF_BUFF	; DI <- buffer
		call	DevWrite	; write data

		pushf			; push flags
		mov	ax,WF_CHSIZE	; AX <- size of chain
		sub	ax,cx		; AX <- readed bytes
		add	WF_WRITTEN,ax	; increase readed counter
		add	WF_BUFF,ax	; shift buffer address
		and	word WF_CHSIZE,byte 0 ; clear size of chain
		popf			; pop flags

WriteFileChFls6:pop	di		; pop DI
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

%undef WF_FILE
%undef WF_WRITTEN
%undef WF_SIZEREM
%undef WF_BUFF
%undef WF_CHSIZE
%undef WF_CHOFFH
%undef WF_CHOFFL
%undef WF_CHCLUST

; -----------------------------------------------------------------------------
;                  Flush file (write directory entry if needed)
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure (must be open)
; OUTPUT:	CY = error
; -----------------------------------------------------------------------------

; ------------- push registers

FlushFile:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

; ------------- check if file is modified

		test	byte [di+FILE_Attr],DIR_Dirty ; is file dirty?
		jz	short FlushFile9 ; file is not dirty
		and	byte [di+FILE_Attr],~DIR_Dirty ; clear dirty flag

; ------------- device and root directory does not have dir entry

		test	byte [di+FILE_Attr],DIR_Device ; device?
		jnz	short FlushFile9 ; device
		cmp	byte [di+FILE_Name],PATHCHAR ; root?
		je	short FlushFile9 ; root

; ------------- get system disk descriptor -> BX

		call	FileSDD		; get disk descriptor -> BX
		jc	short FlushFile9 ; error

;-------------- load sector with directory entry

		mov	ax,[di+FILE_DirL] ; AX <- sector LOW
		mov	dh,0		; DH <- 0
		mov	dl,[di+FILE_DirH] ; DL <- sector HIGH
		add	ax,[bx+SDD_StartSect] ; AX <- absolute sector LOW
		adc	dx,[bx+SDD_StartSect+2] ; DX <- absolute sector HIGH
		call	SDiskReadSect	; read sector into cache
		jc	short FlushFile9 ; error

; ------------- copy directory entry

		push	di		; push DI
		mov	al,[di+FILE_DirInx] ; AL <- dir entry
		and	al,0fh		; AX <- index of entry in sector
		mov	ah,DIR_size	; AH <- size of directory entry
		mul	ah		; AX <- offset of directory entry
		add	si,ax		; SI <- address of directory entry
		xchg	si,di		; SI <- FILE entry, DI <- DIR entry
		mov	cx,DIR_size/2	; CX <- size of directory entry
		rep	movsw		; copy directory entry
		pop	di		; pop DI

; ------------- write sector

		call	BDiskWriteSect	; write sector

; ------------- pop registers

FlushFile9:	pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                         Close file
; -----------------------------------------------------------------------------
; INPUT:	DI = pointer to FILE structure (must be open)
; OUTPUT:	CY = error
; -----------------------------------------------------------------------------

; ------------- flush file

CloseFile:	call	FlushFile	; flush file

; ------------- destroy file descriptor

		pushf			; push flags
		call	ClearFileDesc	; clear file descriptor
		popf			; pop flags
		ret

; -----------------------------------------------------------------------------
;                        Create and open file or directory
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT filename (with path)
;		DL = attributes (incl. DIR_DIR)
;		DI = pointer to destination FILE structure
; OUTPUT:	CY = error
; -----------------------------------------------------------------------------

; ------------- push registers

CreateFileDir:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	bp		; push BP

; ------------- find file name -> SI

		push	dx		; push DX
		mov	ax,"\/"		; separator 1 and 2
		mov	dl,":"		; separator 3
		call	TextFind3ListLast ; find last separator
		pop	dx		; pop DX
		inc	si		; SI <- start of name

; ------------- current directory start cluster -> AX, and SDD descriptor -> BP

		mov	ax,[CurPathFILE+FILE_Cluster];AX<-current path cluster
		push	bx		; push BX
		push	di		; push DI
		mov	di,CurPathFILE	; DI <- current path FILE
		call	FileSDD		; get SDD descriptor from file -> BX
		mov	bp,bx		; BP <- SDD descriptor
		pop	di		; pop DI
		pop	bx		; pop BX

; ------------- get directory start cluster -> AX, and SDD descriptor -> BP

		or	si,si		; is any path?	
		jz	short CreateFileDir4 ; no path

		push	bx		; push BX

		mov	cx,si		; CX <- path length
		call	TextLeft	; get path -> AX
		jc	short CreateFileDir3 ; error
		xchg	ax,bx		; BX <- path

		call	OpenDir		; open directory
		mov	ax,[di+FILE_Cluster] ; AX <- directory cluster

		pushf			; push flags
		call	TextFree	; delete temporary text
		call	FileSDD		; get SDD descriptor from file -> BX
		mov	bp,bx		; BP <- SDD descriptor
		popf			; pop flags		

CreateFileDir3:	pop	bx		; pop BX
		jnc	short CreateFileDir4
CreateFileDir92:jmp	CreateFileDir9	; error

; ------------- check if such dir entry already exists

CreateFileDir4:	mov	cx,[bx+TEXT_Length] ; CX <- text length
		sub	cx,si		; CX <- file name length
		lea	si,[bx+TEXT_Text+si] ; SI <- file name text
		push	dx		; push DX
		xor	dx,dx		; DX <- 0, attributes do not matter
		mov	bx,bp		; BX <- SDD descriptor
		mov	bp,1		; 1 file
		call	FindDirEntry	; find directory entry
		pop	dx		; pop DX
		cmc			; CY = file found OK
		jc	short CreateFileDir92 ; error, file already exists

; ------------- find first deleted entry

		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI

		xor	dx,dx		; DX <- 0, attributes do not matter
		mov	si,DeletedFileMask ; SI <- deleted file mask
		mov	cx,4		; CX <- length of deleted file mask
		mov	bp,1		; 1 file
		call	FindDirEntry	; find first deleted file

		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		jnc	short CreateFileDir5 ; found OK

; ------------- find first unused entry

		push	cx		; push CX
		push	dx		; push DX

		xor	dx,dx		; DX <- 0, attributes do not matter
		xor	cx,cx		; CX <- 0, length of unused file mask
		mov	bp,1		; 1 file
		call	FindDirEntry	; find first deleted file

		pop	dx		; pop DX
		pop	cx		; pop CX
		jnc	short CreateFileDir5 ; found OK

; ------------- increase directory size

		or	ax,ax		; root ?
		stc			; set zero flag
		jz	short CreateFileDir92 ; root
		call	CreateFileDirS	; increase directory size
		jc	short CreateFileDir92 ; error

; ------------- find first unused entry

		push	cx		; push CX
		push	dx		; push DX

		xor	dx,dx		; DX <- 0, attributes do not matter
		xor	cx,cx		; CX <- 0, length of unused file mask
		mov	bp,1		; 1 file
		call	FindDirEntry	; find first deleted file

		pop	dx		; pop DX
		pop	cx		; pop CX
		jc	short CreateFileDir9 ; error

; ------------- initilize entry
;	here is BX = SDD descriptor, DL = attributes, AX = directory cluster

CreateFileDir5:	xchg	ax,bp		; BP <- directory start cluster
		mov	dh,[di+FILE_DirInx] ; DH <- entry index
		push	dx		; push DX (DL=attributes, DH=index)

		mov	ax,[di+FILE_DirDW] ; AX <- sector LOW
		mov	dx,[di+FILE_DirDW+2] ; DX <- sector HIGH
		call	ClearFileDesc	; clear FILE entry
		mov	[di+FILE_DirDW],ax ; return sector LOW
		mov	[di+FILE_DirDW+2],dx ; return sector HIGH

		mov	dh,dl
		mov	dl,ah
		mov	ah,al
		mov	al,0		; DX:AX <- sector * 256
		shl	ax,1
		rcl	dx,1		; DX:AX <- sector offset in bytes

		call	ModifyFile	; set current date and time
		call	PrepFileMask	; prepare filename
		pop	cx		; CL <- attributes, CH <- entry index
		jc	short CreateFileDir9 ; cannot contain "?" characters

		cmp	byte [di],"."	; invalid name
		je	short CreateFileDir54 ; invalid file name
		cmp	byte [di+8]," " ; check extension
		jne	short CreateFileDir56 ; valid name
		cmp	byte [di]," "	; invalid name
CreateFileDir54:stc			; set error flag
		je	short CreateFileDir9 ; invalid file name

; ------------- set attributes

CreateFileDir56:and	cl,DIR_Mask	; mask valid attributes
		mov	byte [di+FILE_Attr],cl ; set attributes

; ------------- invalidate start cluster

		mov	word [di+FILE_Cluster],FAT16_END ; FAT16 end
		test	byte [bx+SDD_FlagsDisk],SDDF_FAT12 ; FAT12 ?
		jz	short CreateFileDir6 ; no
		mov	byte [di+FILE_Cluster+1],FAT12_END/256 ; FAT12 end

; ------------- create first cluster of directory

CreateFileDir6:	call	CreateFileDirD	; initialize directory
		jc	short CreateFileDir9 ; error

; ------------- add entry offset -> DX:AX

CreateFileDir7:	mov	bl,ch		; BL <- disk
		mov	cl,4
		shl	ch,cl		; CH <- index * 16
		mov	cl,ch		; CL <- index * 16
		mov	ch,0
		shl	cx,1		; CX <- offset in sector
		add	ax,cx
		adc	dx,byte 0	; DX:AX <- offset of entry

; ------------- write DIR entry

		and	bl,FILEDEV_MASKINX ; mask index -> system disk type
		mov	cx,DIR_size	; CX <- entry size
		call	DevWrite	; write data

; ------------- pop registers

CreateFileDir9:	pop	bp		; pop BP
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
CreateFileDirD9:ret

; ============= initialize new directory
; CL = attributes, DI = FILE structure, BX = SDD descriptor, BP = dir. cluster

; ------------- check if it is directory

CreateFileDirD:	test	cl,DIR_DIR	; directory?
		jz	short CreateFileDirD9 ; not directory

; ------------- push registers

		push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI

; ------------- find free cluster

		call	FindFreeClust	; find free cluster
		jnc	short CreateFileDirD2
CreateFileDirD1:jmp	CreateFileDirD8 ; not found
CreateFileDirD2:mov	[di+FILE_Cluster],ax ; set cluster

; ------------- mark this cluster as used

		xchg	ax,dx		; DX <- cluster
		mov	ax,FAT16_END	; AX <- end mark
		call	SetNextClust	; mark cluster as last cluster
		jc	short CreateFileDirD1 ; error
		call	FlushFAT	; flush FAT buffer
		jc	short CreateFileDirD1 ; error

; ------------- prepare absolute sector of cluster

		xchg	ax,dx		; AX <- cluster
		call	GetClustSect	; get absolute sector
		mov	[OldSect],ax	; sector number LOW
		mov	[OldSect+2],dx	; sector number HIGH
		mov	cl,[bx+SDD_SectClust] ; CL <- sectors per cluster
		mov	ch,0
		mov	bx,[bx+SDD_BDD]	; BX <- BDD descriptor
		mov	[OldSectBDD],bx	; set old BDD descriptor

; ------------- clear sector buffer

		mov	dx,[di+FILE_Cluster] ; DX <- this cluster
		mov	di,SectBuf	; DI <- sector buffer
		push	di		; push DI
		push	cx		; push CX
		xor	ax,ax		; AX <- 0
		mov	cx,SECTSIZE/2	; CX <- sector size
		rep	stosw		; clear sector buffer
		pop	cx		; pop CX
		pop	di		; pop DI

; ------------- initialize first 2 entries

		call	ClearFileDesc	; clear FILE entry
		call	ModifyFile	; set current date and time
		mov	byte [di],"."	; mark as "THIS" directory
		mov	byte [di+DIR_Attr],DIR_DIR ; set attributes
		mov	[di+DIR_Cluster],dx ; set current cluster

		add	di,byte 32	; DI <- second DIR entry
		call	ClearFileDesc	; clear FILE entry
		call	ModifyFile	; set current date and time
		mov	word [di],".."	; mark as "UP" directory
		mov	byte [di+DIR_Attr],DIR_DIR ; set attributes
		mov	[di+DIR_Cluster],bp ; set parent cluster

; ------------- write one sector

CreateFileDirD4:call	BDiskWriteSect	; write sector from buffer
		jc	short CreateFileDirD8 ; error

; ------------- clear sector

		push	cx		; push CX
		mov	di,SectBuf	; DI <- sector buffer
		xor	ax,ax		; AX <- 0
		mov	cx,SECTSIZE/2	; CX <- sector size
		rep	stosw		; clear sector buffer
		pop	cx		; pop CX

; ------------- next sector

		add	word [OldSect],byte 1 ; increase sector number
		adc	word [OldSect+2],byte 0 ; carry
		loop	CreateFileDirD4	; next sector
		and	word [OldSectBDD],byte 0 ; invalidate BDD descriptor

; ------------- pop registers

CreateFileDirD8:pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; ============= increase directory size
; BX = SDD descriptor, AX = directory cluster

; ------------- push registers

CreateFileDirS:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	di		; push DI

; ------------- find last cluster -> DX

CreateFileDirS2:mov	dx,ax		; DX <- current cluster
		call	GetNextClust	; get next cluster -> AX
		jc	short CreateFileDirS8 ; error
		cmp	ax,byte -1	; end of chain?
		jne	short CreateFileDirS2 ; get next cluster

; ------------- get next cluster

		mov	[LastFreeClust],dx ; last free cluster
		call	FindFreeClust	; find next free cluster -> AX
		jc	short CreateFileDirS8 ; error

; ------------- link to previous cluster

		call	SetNextClust	; set next cluster
		jc	short CreateFileDirS8 ; error
		call	FlushFAT	; flush FAT buffer
		jc	short CreateFileDirS8 ; error

; ------------- mark end of chain

		xchg	ax,dx		; DX <- new current cluster
		mov	ax,FAT16_END	; AX <- end mark
		call	SetNextClust	; mark end cluster
		jc	short CreateFileDirS8 ; error
		call	FlushFAT	; flush FAT buffer
		jc	short CreateFileDirS8 ; error

; ------------- prepare absolute sector of cluster

		xchg	ax,dx		; AX <- cluster
		call	GetClustSect	; get absolute sector
		mov	[OldSect],ax	; sector number LOW
		mov	[OldSect+2],dx	; sector number HIGH

; ------------- clear sector

		mov	di,SectBuf	; DI <- sector buffer
		xor	ax,ax		; AX <- 0
		mov	cx,SECTSIZE/2	; CX <- sector size
		rep	stosw		; clear sector buffer

; ------------- get number of sectors -> CX

		mov	cl,[bx+SDD_SectClust] ; CL <- sectors per cluster
		mov	ch,0
		mov	bx,[bx+SDD_BDD]	; BX <- BDD descriptor
		mov	[OldSectBDD],bx	; set old BDD descriptor

; ------------- write sectors

CreateFileDirS4:call	BDiskWriteSect	; write sector from buffer
		jc	short CreateFileDirS8 ; error
		add	word [OldSect],byte 1 ; increase sector number
		adc	word [OldSect+2],byte 0 ; carry
		loop	CreateFileDirS4	; next sector
		and	word [OldSectBDD],byte 0 ; invalidate BDD descriptor

; ------------- pop registers

CreateFileDirS8:pop	di		; pop DI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                          Create and open file
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT filename (with path)
;		DI = pointer to destination FILE structures
; OUTPUT:	CY = error
; -----------------------------------------------------------------------------

CreateFile:	push	dx		; push DX
		mov	dl,0		; DL <- attributes
		call	CreateFileDir	; create file
		pop	dx		; pop DX
		ret

; -----------------------------------------------------------------------------
;                          Create directory
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT dir name (with path)
; OUTPUT:	CY = error
; -----------------------------------------------------------------------------

CreateDir:	push	dx		; push DX
		push	di		; push DI
		push	bp		; push BP
		mov	bp,sp		; BP <- push SP

		sub	sp,FILE_size	; local buffer
		mov	di,sp		; DI <- local buffer	
		mov	dl,DIR_DIR	; DL <- attributes
		call	CreateFileDir	; create file

		mov	sp,bp		; pop SP
		pop	bp		; pop BP
		pop	di		; pop DI
		pop	dx		; pop DX
		ret

; -----------------------------------------------------------------------------
;                        Delete file or directory
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT filename (with path)
;		DL = required attributes
;		DH = mask of attributes
; OUTPUT:	CY = error
; -----------------------------------------------------------------------------

; ------------- push registers

DeleteFileDir:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	dx		; push DX
		push	si		; push SI
		push	di		; push DI
		push	bp		; push BP

; ------------- temporary FILE descriptor

		sub	sp,FILE_size	; local variables
		mov	di,sp		; DI <- FILE descriptor

; ------------- find and open file

		or	dh,DIR_THISDIR+DIR_UPDIR ; including ".." and "."
		and	dl,~(DIR_THISDIR+DIR_UPDIR) ; not ".." or "."
		mov	bp,1		; BP <- 1 entry to find
		call	FindFiles	; find and open file
		jc	short DeleteFileDir9 ; file not found

; ------------- do some checks

		mov	al,[di+FILE_Name] ; AL <- first character of name
		cmp	al,0		; free entry
		je	short DeleteFileDir8 ; free entry is not valid
		cmp	al,FATDELCHAR	; deleted entry
		je	short DeleteFileDir8 ; deleted entry is not valid
		cmp	al,PATHCHAR	; root?
		je	short DeleteFileDir8 ; root is not valid
		test	byte [di+FILE_Attr],DIR_Device ; device?
		jnz	short DeleteFileDir8 ; device is not valid

; ------------- get SDD descriptor -> BX

		call	FileSDD		; get SDD descriptor -> BX
		jc	short DeleteFileDir9 ; invalid disk

; ------------- check directory, if it is empty

		mov	ax,[di+FILE_Cluster] ; AX <- first cluster
		test	byte [di+FILE_Attr],DIR_DIR ; directory?
		jz	short DeleteFileDir2 ; not directory

		mov	si,DeletedFileAll ; SI <-"*.*"
		mov	cx,3		; CX <- mask length
		xor	dx,dx		; DX <- 0, any files
		xor	di,di		; DI <- 0, only test
		mov	bp,3		; BP <- 3, number of files
		call	FindDirEntry	; find directory entry
		mov	di,sp		; DI <- FILE descriptor
		jc	short DeleteFileDir9 ; error
		cmp	bp,3		; any files except "." and ".." ?
		jae	short DeleteFileDir8 ; error, any files

; ------------- prepare end of chain flag -> SI

DeleteFileDir2:	mov	si,FAT12_RESMIN	; SI <- FAT12 invalid cluster
		test	byte [bx+SDD_FlagsDisk],SDDF_FAT16 ; FAT16 ?
		jz	short DeleteFileDir4 ; no
		mov	si,FAT16_RESMIN	; SI <- FAT16 invalid cluster

; ------------- delete clusters

DeleteFileDir4:	cmp	ax,si		; end of chain reached?
		jae	short DeleteFileDir6 ; end of chain has been found
		cmp	ax,FAT16_MIN	; check cluster
		jb	short DeleteFileDir9 ; disk error
		mov	dx,ax		; DX <- new current cluster
		call	GetNextClust	; get next cluster -> AX
		jc	short DeleteFileDir9 ; error
		xchg	ax,cx		; CX <- next cluster
		mov	ax,FAT16_FREE	; AX <- free mark
		call	SetNextClust	; set next cluster (mark it free)
		jc	short DeleteFileDir9 ; error
		xchg	ax,cx		; AX <- next cluster
		jmp	short DeleteFileDir4 ; next cluster

DeleteFileDir6:	call	FlushFAT	; flush FAT sectors
		jc	short DeleteFileDir9 ; error

; ------------- mark this entry as deleted

		mov	byte [di+FILE_Name],FATDELCHAR ; delete mark
		or	byte [di+FILE_Attr],DIR_Dirty ; dirty flag
		call	FlushFile	; write directory entry
		jmp	short DeleteFileDir9

; ------------- pop registers

DeleteFileDir8:	stc			; set error flag
DeleteFileDir9:	lea	sp,[di+FILE_size] ; pop SP
		pop	bp		; pop BP
		pop	di		; pop DI
		pop	si		; pop SI
		pop	dx		; pop DX
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                              Delete file
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT file name (with path)
; OUTPUT:	CY = error or file is R/O.
; -----------------------------------------------------------------------------

DeleteFile:	push	dx		; push DX
		mov	dx,(DIR_DIR+DIR_RO)*256	; DX <- attributes
		call	DeleteFileDir	; delete file
		pop	dx		; pop DX
		ret

; -----------------------------------------------------------------------------
;                              Delete directory
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT directory name (with path)
; OUTPUT:	CY = error, directory is R/O or it is not empty.
; -----------------------------------------------------------------------------

DeleteDir:	push	dx		; push DX
		mov	dx,(DIR_DIR+DIR_RO)*256+DIR_DIR	; DX <- attributes
		call	DeleteFileDir	; delete file
		pop	dx		; pop DX
		ret

; -----------------------------------------------------------------------------
;                        Rename file or directory (in place)
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT file name (with path)
;		AX = TEXT file new name (without path, can contain ? and *)
; OUTPUT:	CY = error (new name cannot be with path)
; -----------------------------------------------------------------------------

; ------------- push registers

RenameFileDir:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	si		; push SI
		push	di		; push DI
		push	bp		; push BP
		sub	sp,FILE_size	; local buffer
		mov	di,sp		; DI <- local FILE descriptor

; ------------- open source file

		mov	dx,(DIR_THISDIR+DIR_UPDIR)*256 ; attributes
		mov	bp,1		; BP <- max. number of files
		call	FindFiles	; open file
		jc	short RenameFileDir9 ; cannot open file

; ------------- check if new name has path

		xchg	ax,bx		; BX <- new name
		mov	ax,"\/"		; separator 1 and 2
		mov	dl,":"		; separator 3
		call	TextFind3ListLast ; find last separator
		cmc			; CY = separator found
		jc	short RenameFileDir9 ; error, cannot be with path

; ------------- prepare destination mask

		push	di		; push DI
		lea	si,[bx+TEXT_Text] ; SI <- filename text
		mov	cx,[bx+TEXT_Length] ; CX <- filename length
		mov	di,SFileMask	; DI <- file mask buffer
		call	PrepFileMask	; prepare file mask
		mov	si,di		; SI <- file mask
		pop	di		; pop DI

; ------------- rename file

		push	di		; push DI
		mov	cx,11		; CX <- filename length
RenameFileDir4:	lodsb			; AL <- load character
		inc	di		; increase destination pointer
		cmp	al,"?"		; let old character?
		je	short RenameFileDir5 ; skip this character
		dec	di		; decrease destination pointer
		stosb			; store new character
RenameFileDir5:	loop	RenameFileDir4	; next character
		pop	di		; pop DI

; ------------- save new directory entry

		or	byte [di+FILE_Attr],DIR_Dirty ; set dirty flag
		call	FlushFile	; save directory entry

; ------------- pop registers

RenameFileDir9:	lea	sp,[di+FILE_size] ; pop SP
		pop	bp		; pop BP
		pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                               Move file
; -----------------------------------------------------------------------------
; INPUT:	BX = TEXT file name (with path)
;		AX = TEXT file new name (with path, cannot contain ? or *)
; OUTPUT:	CY = error (cannot move to another disk)
; -----------------------------------------------------------------------------

; ------------- push registers

MoveFile:	push	ax		; push AX
		push	bx		; push BX
		push	cx		; push CX
		push	si		; push SI
		push	di		; push DI
		push	bp		; push BP
		sub	sp,2*FILE_size	; local buffer

; ------------- open source file

		mov	di,sp		; DI <- source FILE descriptor
		mov	dx,(DIR_THISDIR+DIR_UPDIR+DIR_DIR)*256 ; attributes
		mov	bp,1		; BP <- max. number of files
		call	FindFiles	; open file
		jc	short MoveFile9 ; cannot open file

; ------------- create destination file

		mov	si,di		; SI <- source FILE descriptor
		add	di,byte FILE_size ; DI <- destination FILE descriptor
		xchg	ax,bx		; BX <- new name
		call	CreateFile	; create file
		jc	short MoveFile9 ; cannot create new file

; ------------- check disk

		mov	al,[si+FILE_DirInx] ; AL <- source disk
		mov	ah,[di+FILE_DirInx] ; AH <- destination disk
		and	ax,0f0f0h	; mask disk
		cmp	al,ah		; check disk
		je	short MoveFile4	; disk is OK

		mov	byte [di+FILE_Name],FATDELCHAR ; delete character
		or	byte [di+FILE_Attr],DIR_Dirty ; set dirty flag
		call	FlushFile	; save directory entry
		stc			; set error flag
		jmp	short MoveFile9

; ------------- copy file parameters

MoveFile4:	push	di		; push DI
		add	si,FILE_Time	; SI <- time entry
		add	di,FILE_Time	; DI <- time entry
		mov	cl,10/2		; CX <- time, date, cluster, size
		rep	movsw		; copy other entries
		pop	di		; pop DI

; ------------- save new directory entry

		or	byte [di+FILE_Attr],DIR_Dirty ; set dirty flag
		call	FlushFile	; save directory entry
		jc	short MoveFile9	; error

; ------------- delete source entry

		mov	di,sp		; DI <- source entry
		mov	byte [di+FILE_Name],FATDELCHAR ; delete character
		or	byte [di+FILE_Attr],DIR_Dirty ; set dirty flag
		call	FlushFile	; save directory entry

; ------------- pop registers

MoveFile9:	mov	di,sp		; DI <- local buffers
		lea	sp,[di+2*FILE_size] ; pop SP
		pop	bp		; pop BP
		pop	di		; pop DI
		pop	si		; pop SI
		pop	cx		; pop CX
		pop	bx		; pop BX
		pop	ax		; pop AX
		ret

; -----------------------------------------------------------------------------
;                            Constant data
; -----------------------------------------------------------------------------

		CONST_SECTION

DeletedFileMask:db	FATDELCHAR	; deleted file mask (length: 4)
DeletedFileAll:	db	"*.*"

; -----------------------------------------------------------------------------
;                            Uninitialised data
; -----------------------------------------------------------------------------

		DATA_SECTION

LastFATSect:	resd	1		; last FAT absolute sector
ModiFAT:	resb	1		; 1=FAT sector is modified, 0=no
LastFreeClust:	resw	1		; last allocated free cluster

; ------------- current path

		align	4, resb 1
CurPathFILE:	resb	FILE_size	; current directory
CurPath:	resb	PATHMAX+2	; CTEXT current path

; ------------- search file

		align	4, resb 1
FindFileFILE:	resb	FILE_size	; FindFile temporaty FILE structure
SFileMask:	resb	11		; search filename mask
