;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;; RAMDISK functions                                            ;;
;; (C) 2004 Ville Turjanmaa, License: GPL                       ;;
;; Addings by M.Lisovin                                         ;;
;; LFN support by diamond                                       ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision$


; calculate fat chain

calculatefatchain:

   pushad

   mov	esi,RAMDISK+512
   mov	edi,RAMDISK_FAT

 fcnew:
   mov	eax,dword [esi]
   mov	ebx,dword [esi+4]
   mov	ecx,dword [esi+8]
   mov	edx,ecx
   shr	edx,4	;8 ok
   shr	dx,4	;7 ok
   xor	ch,ch
   shld ecx,ebx,20 ;6 ok
   shr	cx,4	 ;5 ok
   shld ebx,eax,12
   and	ebx,0x0fffffff	;4 ok
   shr	bx,4	;3 ok
   shl	eax,4
   and	eax,0x0fffffff	;2 ok
   shr	ax,4  ;1 ok
   mov	dword [edi],eax
   mov	dword [edi+4],ebx
   mov	dword [edi+8],ecx
   mov	dword [edi+12],edx
   add	edi,16
   add	esi,12

   cmp	edi,RAMDISK_FAT+2856*2	 ;2849 clusters
   jnz	fcnew

   popad
   ret


restorefatchain:   ; restore fat chain

   pushad

   mov	esi,RAMDISK_FAT
   mov	edi,RAMDISK+512

  fcnew2:
   mov	eax,dword [esi]
   mov	ebx,dword [esi+4]
   shl	ax,4
   shl	eax,4
   shl	bx,4
   shr	ebx,4
   shrd eax,ebx,8
   shr	ebx,8
   mov	dword [edi],eax
   mov	word [edi+4],bx
   add	edi,6
   add	esi,8

   cmp	edi,RAMDISK+512+4278	 ;4274 bytes - all used FAT
   jb	fcnew2

   mov	esi,RAMDISK+512 	  ; duplicate fat chain
   mov	edi,RAMDISK+512+0x1200
   mov	ecx,1069	;4274/4
   cld
   rep	movsd

   popad
   ret


ramdisk_free_space:
;---------------------------------------------
;
; returns free space in edi
; rewr.by Mihasik
;---------------------------------------------

	push   eax ebx ecx

	mov  edi,RAMDISK_FAT ;start of FAT
	xor  ax,ax    ;Free cluster=0x0000 in FAT
	xor  ebx,ebx  ;counter
	mov  ecx,2849 ;2849 clusters
	cld
    rdfs1:
	repne scasw
	jnz  rdfs2    ;if last cluster not 0
	inc  ebx
	test	ecx, ecx
	jnz	rdfs1
    rdfs2:
	shl  ebx,9    ;free clusters*512
	mov  edi,ebx

	pop    ecx ebx eax
	ret


expand_filename:
;---------------------------------------------
;
; exapand filename with '.' to 11 character
; eax - pointer to filename
;---------------------------------------------

	push esi edi ebx

	mov  edi,esp		      ; check for '.' in the name
	add  edi,12+8

	mov  esi,eax

	mov  eax,edi
	mov  [eax+0],dword '    '
	mov  [eax+4],dword '    '
	mov  [eax+8],dword '    '

      flr1:

	cmp  [esi],byte '.'
	jne  flr2
	mov  edi,eax
	add  edi,7
	jmp  flr3

      flr2:

	mov  bl,[esi]
	mov  [edi],bl

      flr3:

	inc  esi
	inc  edi

	mov  ebx,eax
	add  ebx,11

	cmp  edi,ebx
	jbe  flr1

	pop  ebx edi esi
	ret

fileread:
;----------------------------------------------------------------
;
;  fileread - sys floppy
;
;  eax  points to filename 11 chars
;  ebx  first wanted block       ; 1+ ; if 0 then set to 1
;  ecx  number of blocks to read ; 1+ ; if 0 then set to 1
;  edx  mem location to return data
;  esi  length of filename 12*X 0=root
;
;  ret ebx = size or 0xffffffff file not found
;      eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
	test   ebx,ebx ;if ebx=0 - set to 1
	jnz    frfl5
	inc    ebx
      frfl5:
	test   ecx,ecx ;if ecx=0 - set to 1
	jnz    frfl6
	inc    ecx
      frfl6:
	test   esi,esi		; return ramdisk root
	jnz    fr_noroot	;if not root
	cmp    ebx,14		;14 clusters=root dir
	ja     oorr
	cmp    ecx,14
	ja     oorr
	jmp    fr_do
      oorr:
	mov    eax,5		;out of root range (fnf)
	xor    ebx,ebx
	dec    ebx		;0xffffffff
	ret

      fr_do:			;reading rootdir
	mov    edi,edx
	dec    ebx
	push   edx
	mov    edx,ecx
	add    edx,ebx
	cmp    edx,15	  ;ebx+ecx=14+1
	pushf
	jbe    fr_do1
	sub    edx,14
	sub    ecx,edx
      fr_do1:
	shl    ebx,9
	mov    esi,RAMDISK+512*19
	add    esi,ebx
	shl    ecx,7
	cld
	rep    movsd
	popf
	pop    edx
	jae    fr_do2
	xor    eax,eax ; ok read
	xor    ebx,ebx
	ret
      fr_do2:	     ;if last cluster
	mov    eax,6  ;end of file
	xor    ebx,ebx
	ret

     fr_noroot:

	sub    esp,32
	call   expand_filename

	dec    ebx

	push   eax

	push   eax ebx ecx edx esi edi
	call   rd_findfile
	je     fifound
	add    esp,32+28   ;if file not found
	ret

     fifound:

	mov    ebx,[edi-11+28]		;file size
	mov    [esp+20],ebx
	mov    [esp+24],ebx
	add    edi,0xf
	movzx  eax,word [edi]
	mov    edi,eax			;edi=cluster

      frnew:

	add    eax,31			;bootsector+2*fat+filenames
	shl    eax,9			;*512
	add    eax,RAMDISK	       ;image base
	mov    ebx,[esp+8]
	mov    ecx,512			;[esp+4]

	cmp    [esp+16],dword 0 	; wanted cluster ?
	jne    frfl7
	call   memmove
	add    [esp+8],dword 512
	dec    dword [esp+12]		; last wanted cluster ?
	je     frnoread
	jmp    frfl8
      frfl7:
	dec    dword [esp+16]
      frfl8:
	movzx  eax,word [edi*2+RAMDISK_FAT]	   ; find next cluster from FAT
	mov    edi,eax
	cmp    edi,4095 		;eof  - cluster
	jz     frnoread2

	cmp    [esp+24],dword 512	;eof  - size
	jb     frnoread
	sub    [esp+24],dword 512

	jmp    frnew

      frnoread2:

	cmp    [esp+16],dword 0 	; eof without read ?
	je     frnoread

	pop    edi esi edx ecx
	add    esp,4
	pop    ebx     ; ebx <- eax : size of file
	add    esp,36
	mov    eax,6   ; end of file
	ret

      frnoread:

	pop    edi esi edx ecx
	add    esp,4
	pop    ebx     ; ebx <- eax : size of file
	add    esp,36
	xor    eax,eax	;read ok
	ret



   rd_findfile:
   ;by Mihasik
   ;IN: eax - pointer to filename OUT: filestring+11 in edi or notZero in flags and fnf in eax,ebx

	mov    edi,RAMDISK+512*18+512  ;Point at directory
	cld
    rd_newsearch:
	mov    esi,eax
	mov    ecx,11
	rep    cmpsb
	je     rd_ff
	add    cl,21
	add    edi,ecx
	cmp    edi,RAMDISK+512*33
	jb     rd_newsearch
	mov    eax,5	  ;if file not found - eax=5
	xor    ebx,ebx
	dec    ebx    ;ebx=0xffffffff and zf=0
     rd_ff:
	ret

; \begin{diamond}

uni2ansi_str:
; convert UNICODE zero-terminated string to ASCII-string (codepage 866)
; in: esi->source, edi->buffer (may be esi=edi)
; destroys: eax,esi,edi
	lodsw
	test	ax, ax
	jz	.done
	cmp	ax, 0x80
	jb	.ascii
	cmp	ax, 0x401
	jz	.yo1
	cmp	ax, 0x451
	jz	.yo2
	cmp	ax, 0x410
	jb	.unk
	cmp	ax, 0x440
	jb	.rus1
	cmp	ax, 0x450
	jb	.rus2
.unk:
	mov	al, '_'
	jmp	.doit
.yo1:
	mov	al, '�'
	jmp	.doit
.yo2:
	mov	al, '�'
	jmp	.doit
.rus1:
; 0x410-0x43F -> 0x80-0xAF
	add	al, 0x70
	jmp	.doit
.rus2:
; 0x440-0x44F -> 0xE0-0xEF
	add	al, 0xA0
.ascii:
.doit:
	stosb
	jmp	uni2ansi_str
.done:
	mov	byte [edi], 0
	ret

ansi2uni_char:
; convert ANSI character in al to UNICODE character in ax, using cp866 encoding
	mov	ah, 0
; 0x00-0x7F - trivial map
	cmp	al, 0x80
	jb	.ret
; 0x80-0xAF -> 0x410-0x43F
	cmp	al, 0xB0
	jae	@f
	add	ax, 0x410-0x80
.ret:
	ret
@@:
; 0xE0-0xEF -> 0x440-0x44F
	cmp	al, 0xE0
	jb	.unk
	cmp	al, 0xF0
	jae	@f
	add	ax, 0x440-0xE0
	ret
; 0xF0 -> 0x401
; 0xF1 -> 0x451
@@:
	cmp	al, '�'
	jz	.yo1
	cmp	al, '�'
	jz	.yo2
.unk:
	mov	al, '_' 	; ah=0
	ret
.yo1:
	mov	ax, 0x401
	ret
.yo2:
	mov	ax, 0x451
	ret

char_toupper:
; convert character to uppercase, using cp866 encoding
; in: al=symbol
; out: al=converted symbol
	cmp	al, 'a'
	jb	.ret
	cmp	al, 'z'
	jbe	.az
        cmp     al, '�'
        jz      .yo1
	cmp	al, '�'
	jb	.ret
	cmp	al, '�'
	jb	.rus1
	cmp	al, '�'
	ja	.ret
; 0xE0-0xEF -> 0x90-0x9F
	sub	al, '�'-'�'
.ret:
	ret
.rus1:
; 0xA0-0xAF -> 0x80-0x8F
.az:
	and	al, not 0x20
	ret
.yo1:
; 0xF1 -> 0xF0
        dec     ax
        ret

fat_get_name:
; in: edi->FAT entry
; out: CF=1 - no valid entry
; else CF=0 and ebp->ASCIIZ-name
; (maximum length of filename is 255 (wide) symbols without trailing 0,
;  but implementation requires buffer 261 words)
; destroys eax
	cmp	byte [edi], 0
	jz	.no
	cmp	byte [edi], 0xE5
	jnz	@f
.no:
	stc
	ret
@@:
	cmp	byte [edi+11], 0xF
	jz	.longname
	test	byte [edi+11], 8
	jnz	.no
	push	ecx
	push	edi ebp
	test	byte [ebp-4], 1
	jnz	.unicode_short

	mov	eax, [edi]
	mov	ecx, [edi+4]
	mov	[ebp], eax
	mov	[ebp+4], ecx

	mov	ecx, 8
@@:
	cmp	byte [ebp+ecx-1], ' '
	loope	 @b

	mov	eax, [edi+8]
	cmp	al, ' '
	je	.done
	shl	eax, 8
	mov	al, '.'

	lea ebp, [ebp+ecx+1]
	mov	[ebp], eax
	mov	ecx, 3
@@:
	rol eax, 8
	cmp al, ' '
	jne .done
	loop   @b
	dec ebp
.done:
	and	byte [ebp+ecx+1], 0   ; CF=0
	pop	ebp edi ecx
	ret
.unicode_short:
	mov	ecx, 8
	push	ecx
@@:
	mov	al, [edi]
	inc	edi
	call	ansi2uni_char
	mov	[ebp], ax
	inc	ebp
	inc	ebp
	loop	@b
	pop	ecx
@@:
	cmp	word [ebp-2], ' '
	jnz	@f
	dec	ebp
	dec	ebp
	loop	@b
@@:
	mov	word [ebp], '.'
	inc	ebp
	inc	ebp
	mov	ecx, 3
	push	ecx
@@:
	mov	al, [edi]
	inc	edi
	call	ansi2uni_char
	mov	[ebp], ax
	inc	ebp
	inc	ebp
	loop	@b
	pop	ecx
@@:
	cmp	word [ebp-2], ' '
	jnz	@f
	dec	ebp
	dec	ebp
	loop	@b
	dec	ebp
	dec	ebp
@@:
	and	word [ebp], 0	; CF=0
	pop	ebp edi ecx
	ret
.longname:
; LFN
	mov	al, byte [edi]
	and	eax, 0x3F
	dec	eax
	cmp	al, 20
	jae	.no	; ignore invalid entries
	mov	word [ebp+260*2], 0	; force null-terminating for orphans
	imul	eax, 13*2
	add	ebp, eax
	test	byte [edi], 0x40
	jz	@f
	mov	word [ebp+13*2], 0
@@:
	push	eax
; now copy name from edi to ebp ...
	mov	eax, [edi+1]
	mov	[ebp], eax	; symbols 1,2
	mov	eax, [edi+5]
	mov	[ebp+4], eax	; 3,4
	mov	eax, [edi+9]
	mov	[ebp+8], ax	; 5
	mov	eax, [edi+14]
	mov	[ebp+10], eax	; 6,7
	mov	eax, [edi+18]
	mov	[ebp+14], eax	; 8,9
	mov	eax, [edi+22]
	mov	[ebp+18], eax	; 10,11
	mov	eax, [edi+28]
	mov	[ebp+22], eax	; 12,13
; ... done
	pop	eax
	sub	ebp, eax
	test	eax, eax
	jz	@f
; if this is not first entry, more processing required
	stc
	ret
@@:
; if this is first entry:
	test	byte [ebp-4], 1
	jnz	.ret
; buffer at ebp contains UNICODE name, convert it to ANSI
	push	esi edi
	mov	esi, ebp
	mov	edi, ebp
	call	uni2ansi_str
	pop	edi esi
.ret:
	clc
	ret

fat_compare_name:
; compares ASCIIZ-names, case-insensitive (cp866 encoding)
; in: esi->name, ebp->name
; out: if names match: ZF=1 and esi->next component of name
;      else: ZF=0, esi is not changed
; destroys eax
	push	ebp esi
.loop:
	mov	al, [ebp]
	inc	ebp
	call	char_toupper
	push	eax
	lodsb
	call	char_toupper
	cmp	al, [esp]
	jnz	.done
	pop	eax
	test	al, al
	jnz	.loop
	dec	esi
	pop	eax
	pop	ebp
	xor	eax, eax	; set ZF flag
	ret
.done:
	cmp	al, '/'
	jnz	@f
	cmp	byte [esp], 0
	jnz	@f
	mov	[esp+4], esi
@@:
	pop	eax
	pop	esi ebp
	ret

fat_time_to_bdfe:
; in: eax=FAT time
; out: eax=BDFE time
	push	ecx edx
	mov	ecx, eax
	mov	edx, eax
	shr	eax, 11
	shl	eax, 16 ; hours
	and	edx, 0x1F
	add	edx, edx
	mov	al, dl	; seconds
	shr	ecx, 5
	and	ecx, 0x3F
	mov	ah, cl	; minutes
	pop	edx ecx
	ret

fat_date_to_bdfe:
	push	ecx edx
	mov	ecx, eax
	mov	edx, eax
	shr	eax, 9
	add	ax, 1980
	shl	eax, 16 ; year
	and	edx, 0x1F
	mov	al, dl	; day
	shr	ecx, 5
	and	ecx, 0xF
	mov	ah, cl	; month
	pop	edx ecx
	ret

bdfe_to_fat_time:
	push	edx
	mov	edx, eax
	shr	eax, 16
	and	dh, 0x3F
	shl	eax, 6
	or	al, dh
	shr	dl, 1
	and	dl, 0x1F
	shl	eax, 5
	or	al, dl
	pop	edx
	ret

bdfe_to_fat_date:
	push	edx
	mov	edx, eax
	shr	eax, 16
	sub	ax, 1980
	and	dh, 0xF
	shl	eax, 4
	or	al, dh
	and	dl, 0x1F
	shl	eax, 5
	or	al, dl
	pop	edx
	ret

fat_entry_to_bdfe:
; convert FAT entry at edi to BDFE (block of data of folder entry) at esi, advance esi
; destroys eax
	mov	eax, [ebp-4]
	mov	[esi+4], eax	; ASCII/UNICODE name
fat_entry_to_bdfe2:
	movzx	eax, byte [edi+11]
	mov	[esi], eax	; attributes
	movzx	eax, word [edi+14]
	call	fat_time_to_bdfe
	mov	[esi+8], eax	; creation time
	movzx	eax, word [edi+16]
	call	fat_date_to_bdfe
	mov	[esi+12], eax	; creation date
	and	dword [esi+16], 0	; last access time is not supported on FAT
	movzx	eax, word [edi+18]
	call	fat_date_to_bdfe
	mov	[esi+20], eax	; last access date
	movzx	eax, word [edi+22]
	call	fat_time_to_bdfe
	mov	[esi+24], eax	; last write time
	movzx	eax, word [edi+24]
	call	fat_date_to_bdfe
	mov	[esi+28], eax	; last write date
	mov	eax, [edi+28]
	mov	[esi+32], eax	; file size (low dword)
	xor	eax, eax
	mov	[esi+36], eax	; file size (high dword)
	test	ebp, ebp
	jz	.ret
	push	ecx edi
	lea	edi, [esi+40]
	mov	esi, ebp
	test	byte [esi-4], 1
	jz	.ansi
	mov	ecx, 260/2
	rep	movsd
	mov	[edi-2], ax
@@:
	mov	esi, edi
	pop	edi ecx
.ret:
	ret
.ansi:
	mov	ecx, 264/4
	rep	movsd
	mov	[edi-1], al
	jmp	@b

bdfe_to_fat_entry:
; convert BDFE at edx to FAT entry at edi
; destroys eax
; attributes byte
	test	byte [edi+11], 8	; volume label?
	jnz	@f
	mov	al, [edx]
	and	al, 0x27
	and	byte [edi+11], 0x10
	or	byte [edi+11], al
@@:
	mov	eax, [edx+8]
	call	bdfe_to_fat_time
	mov	[edi+14], ax		; creation time
	mov	eax, [edx+12]
	call	bdfe_to_fat_date
	mov	[edi+16], ax		; creation date
	mov	eax, [edx+20]
	call	bdfe_to_fat_date
	mov	[edi+18], ax		; last access date
	mov	eax, [edx+24]
	call	bdfe_to_fat_time
	mov	[edi+22], ax		; last write time
	mov	eax, [edx+28]
	call	bdfe_to_fat_date
	mov	[edi+24], ax		; last write date
	ret

ramdisk_root_first:
	mov	edi, RAMDISK+512*19
	clc
	ret
ramdisk_root_next:
	add	edi, 0x20
	cmp	edi, RAMDISK+512*33
	cmc
	ret

ramdisk_root_extend_dir:
	stc
	ret

uglobal
; this is for delete support
rd_prev_sector		dd	?
rd_prev_prev_sector	dd	?
endg

ramdisk_notroot_next:
	add	edi, 0x20
	test	edi, 0x1FF
	jz	ramdisk_notroot_next_sector
	ret	; CF=0
ramdisk_notroot_next_sector:
	push	ecx
	mov	ecx, [eax]
	push	[rd_prev_sector]
	pop	[rd_prev_prev_sector]
	mov	[rd_prev_sector], ecx
	mov	ecx, [ecx*2+RAMDISK_FAT]
	and	ecx, 0xFFF
	cmp	ecx, 2849
	jae	ramdisk_notroot_first.err2
	mov	[eax], ecx
	pop	ecx
ramdisk_notroot_first:
	mov	eax, [eax]
	cmp	eax, 2
	jb	.err
	cmp	eax, 2849
	jae	.err
	shl	eax, 9
	lea	edi, [eax+(31 shl 9)+RAMDISK]
	clc
	ret
.err2:
	pop	ecx
.err:
	stc
	ret
ramdisk_notroot_next_write:
	test	edi, 0x1FF
	jz	ramdisk_notroot_next_sector
ramdisk_root_next_write:
	ret

ramdisk_notroot_extend_dir:
	pusha
	xor	eax, eax
	mov	edi, RAMDISK_FAT
	mov	ecx, 2849
	repnz	scasw
	jnz	.notfound
	mov	word [edi-2], 0xFFF
	sub	edi, RAMDISK_FAT
	shr	edi, 1
	dec	edi
	mov	eax, [esp+28]
	mov	ecx, [eax]
	mov	[RAMDISK_FAT+ecx*2], di
	mov	[eax], edi
	shl	edi, 9
	add	edi, (31 shl 9)+RAMDISK
	mov	[esp], edi
	xor	eax, eax
	mov	ecx, 128
	rep	stosd
	popa
	clc
	ret
.notfound:
	popa
	stc
	ret

rd_find_lfn:
; in: esi+ebp -> name
; out: CF=1 - file not found
;      else CF=0 and edi->direntry
	push	esi edi
	push	0
	push	ramdisk_root_first
	push	ramdisk_root_next
.loop:
	call	fat_find_lfn
	jc	.notfound
	cmp	byte [esi], 0
	jz	.found
.continue:
	test	byte [edi+11], 10h
	jz	.notfound
	movzx	eax, word [edi+26]
	mov	[esp+8], eax
	mov	dword [esp+4], ramdisk_notroot_first
	mov	dword [esp], ramdisk_notroot_next
	test	eax, eax
	jnz	.loop
	mov	dword [esp+4], ramdisk_root_first
	mov	dword [esp], ramdisk_notroot_next
	jmp	.loop
.notfound:
	add	esp, 12
	pop	edi esi
	stc
	ret
.found:
	test	ebp, ebp
	jz	@f
	mov	esi, ebp
	xor	ebp, ebp
	jmp	.continue
@@:
	mov	eax, [esp+8]
	add	esp, 16 	; CF=0
	pop	esi
	ret

;----------------------------------------------------------------
;
;  fs_RamdiskRead - LFN variant for reading sys floppy
;
;  esi  points to filename
;  ebx  pointer to 64-bit number = first wanted byte, 0+
;       may be ebx=0 - start from first byte
;  ecx  number of bytes to read, 0+
;  edx  mem location to return data
;
;  ret ebx = bytes read or 0xffffffff file not found
;      eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
fs_RamdiskRead:
	cmp	byte [esi], 0
	jnz	@f
	or	ebx, -1
	mov	eax, 10 	; access denied
	ret
@@:
	push	edi
	call	rd_find_lfn
	jnc	.found
	pop	edi
	or	ebx, -1
	mov	eax, 5		; file not found
	ret
.found:
	test	ebx, ebx
	jz	.l1
	cmp	dword [ebx+4], 0
	jz	@f
	xor	ebx, ebx
.reteof:
	mov	eax, 6		; EOF
	pop	edi
	ret
@@:
	mov	ebx, [ebx]
.l1:
	push	ecx edx
	push	0
	mov	eax, [edi+28]
	sub	eax, ebx
	jb	.eof
	cmp	eax, ecx
	jae	@f
	mov	ecx, eax
	mov	byte [esp], 6		; EOF
@@:
	movzx	edi, word [edi+26]	; cluster
.new:
	jecxz	.done
	test	edi, edi
	jz	.eof
	cmp	edi, 0xFF8
	jae	.eof
	lea	eax, [edi+31]		; bootsector+2*fat+filenames
	shl	eax, 9			; *512
	add	eax, RAMDISK	       ; image base
; now eax points to data of cluster
	sub	ebx, 512
	jae	.skip
	lea	eax, [eax+ebx+512]
	neg	ebx
	push	ecx
	cmp	ecx, ebx
	jbe	@f
	mov	ecx, ebx
@@:
	mov	ebx, edx
	call	memmove
	add	edx, ecx
	sub	[esp], ecx
	pop	ecx
	xor	ebx, ebx
.skip:
	movzx	edi, word [edi*2+RAMDISK_FAT]	   ; find next cluster from FAT
	jmp	.new
.eof:
	mov	ebx, edx
	pop	eax edx ecx
	sub	ebx, edx
	jmp	.reteof
.done:
	mov	ebx, edx
	pop	eax edx ecx edi
	sub	ebx, edx
	ret

;----------------------------------------------------------------
;
;  fs_RamdiskReadFolder - LFN variant for reading sys floppy folder
;
;  esi  points to filename; only root is folder on ramdisk
;  ebx  pointer to structure 32-bit number = first wanted block
;                          & flags (bitfields)
; flags: bit 0: 0=ANSI names, 1=UNICODE names
;  ecx  number of blocks to read, 0+
;  edx  mem location to return data
;
;  ret ebx = size or 0xffffffff file not found
;      eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
fs_RamdiskReadFolder:
	push	edi
	cmp	byte [esi], 0
	jz	.root
	call	rd_find_lfn
	jnc	.found
	pop	edi
	or	ebx, -1
	mov	eax, ERROR_FILE_NOT_FOUND
	ret
.found:
	test	byte [edi+11], 0x10
	jnz	.found_dir
	pop	edi
	or	ebx, -1
	mov	eax, ERROR_ACCESS_DENIED
	ret
.found_dir:
	movzx	eax, word [edi+26]
	add	eax, 31
	push	0
	jmp	.doit
.root:
	mov	eax, 19
	push	14
.doit:
	push	esi ecx ebp
	sub	esp, 262*2	; reserve space for LFN
	mov	ebp, esp
	push	dword [ebx+4]	; for fat_get_name: read ANSI/UNICODE names
	mov	ebx, [ebx]
; init header
	push	eax ecx
	mov	edi, edx
	mov	ecx, 32/4
	xor	eax, eax
	rep	stosd
	mov	byte [edx], 1	; version
	pop	ecx eax
	mov	esi, edi	; esi points to block of data of folder entry (BDFE)
.main_loop:
	mov	edi, eax
	shl	edi, 9
	add	edi, RAMDISK
	push	eax
.l1:
	call	fat_get_name
	jc	.l2
	cmp	byte [edi+11], 0xF
	jnz	.do_bdfe
	add	edi, 0x20
	test	edi, 0x1FF
	jnz	.do_bdfe
	pop	eax
	inc	eax
	dec	byte [esp+262*2+16]
	jz	.done
	jns	@f
; read next sector from FAT
	mov	eax, [(eax-31-1)*2+RAMDISK_FAT]
	and	eax, 0xFFF
	cmp	eax, 0xFF8
	jae	.done
	add	eax, 31
	mov	byte [esp+262*2+16], 0
@@:
	mov	edi, eax
	shl	edi, 9
	add	edi, RAMDISK
	push	eax
.do_bdfe:
	inc	dword [edx+8]	; new file found
	dec	ebx
	jns	.l2
	dec	ecx
	js	.l2
	inc	dword [edx+4]  ; new file block copied
	call	fat_entry_to_bdfe
.l2:
	add	edi, 0x20
	test	edi, 0x1FF
	jnz	.l1
	pop	eax
	inc	eax
	dec	byte [esp+262*2+16]
	jz	.done
	jns	@f
; read next sector from FAT
	mov	eax, [(eax-31-1)*2+RAMDISK_FAT]
	and	eax, 0xFFF
	cmp	eax, 0xFF8
	jae	.done
	add	eax, 31
	mov	byte [esp+262*2+16], 0
@@:
	jmp	.main_loop
.done:
	add	esp, 262*2+4
	pop	ebp
	mov	ebx, [edx+4]
	xor	eax, eax
	dec	ecx
	js	@f
	mov	al, ERROR_END_OF_FILE
@@:
	pop	ecx esi edi edi
	ret

iglobal
label fat_legal_chars byte
; 0 = not allowed
; 1 = allowed only in long names
; 3 = allowed
	times 32 db 0
;                 ! " # $ % & ' ( ) * + , - . /
	db	1,3,0,3,3,3,3,3,3,3,0,1,1,3,3,0
;               0 1 2 3 4 5 6 7 8 9 : ; < = > ?
	db	3,3,3,3,3,3,3,3,3,3,0,1,0,1,0,0
;               @ A B C D E F G H I J K L M N O
	db	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
;               P Q R S T U V W X Y Z [ \ ] ^ _
	db	3,3,3,3,3,3,3,3,3,3,3,1,0,1,3,3
;               ` a b c d e f g h i j k l m n o
	db	3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3
;               p q r s t u v w x y z { | } ~
	db	3,3,3,3,3,3,3,3,3,3,3,3,0,3,3,0
endg

fat_name_is_legal:
; in: esi->(long) name
; out: CF set <=> legal
; destroys eax
	push	esi
	xor	eax, eax
@@:
	lodsb
	test	al, al
	jz	.done
	cmp	al, 80h
	jae	.big
	test	[fat_legal_chars+eax], 1
	jnz	@b
.err:
	pop	esi
	clc
	ret
.big:
; 0x80-0xAF, 0xE0-0xEF
	cmp	al, 0xB0
	jb	@b
	cmp	al, 0xE0
	jb	.err
	cmp	al, 0xF0
	jb	@b
	jmp	.err
.done:
	sub	esi, [esp]
	cmp	esi, 257
	pop	esi
	ret

fat_next_short_name:
; in: edi->8+3 name
; out: name corrected
;      CF=1 <=> error
	pushad
	mov	ecx, 8
	mov	al, '~'
	std
	push	edi
	add	edi, 7
	repnz	scasb
	pop	edi
	cld
	jz	.tilde
; tilde is not found, insert "~1" at end
	add	edi, 6
	cmp	word [edi], '  '
	jnz	.insert_tilde
@@:	dec	edi
	cmp	byte [edi], ' '
	jz	@b
	inc	edi
.insert_tilde:
	mov	word [edi], '~1'
	popad
	clc
	ret
.tilde:
	push	edi
	add	edi, 7
	xor	ecx, ecx
@@:
; after tilde may be only digits and trailing spaces
	cmp	byte [edi], '~'
	jz	.break
	cmp	byte [edi], ' '
	jz	.space
	cmp	byte [edi], '9'
	jnz	.found
	dec	edi
	jmp	@b
.space:
	dec	edi
	inc	ecx
	jmp	@b
.found:
	inc	byte [edi]
	add	dword [esp], 8
	jmp	.zerorest
.break:
	jecxz	.noplace
	inc	edi
	mov	al, '1'
@@:
	xchg	al, [edi]
	inc	edi
	cmp	al, ' '
	mov	al, '0'
	jnz	@b
.succ:
	pop	edi
	popad
	clc
	ret
.noplace:
	dec	edi
	cmp	edi, [esp]
	jz	.err
	add	dword [esp], 8
	mov	word [edi], '~1'
	inc	edi
	inc	edi
@@:
	mov	byte [edi], '0'
.zerorest:
	inc	edi
	cmp	edi, [esp]
	jb	@b
	pop	edi
	popad
	;clc    ; automatically
	ret
.err:
	pop	edi
	popad
	stc
	ret

fat_gen_short_name:
; in: esi->long name
;     edi->buffer (8+3=11 chars)
; out: buffer filled
	pushad
	mov	eax, '    '
	push	edi
	stosd
	stosd
	stosd
	pop	edi
	xor	eax, eax
	push	8
	pop	ebx
	lea	ecx, [edi+8]
.loop:
	lodsb
	test	al, al
	jz	.done
	call	char_toupper
	cmp	al, ' '
	jz	.space
	cmp	al, 80h
	ja	.big
	test	[fat_legal_chars+eax], 2
	jnz	.symbol
.inv_symbol:
	mov	al, '_'
	or	bh, 1
.symbol:
	cmp	al, '.'
	jz	.dot
.normal_symbol:
	dec	bl
	jns	.store
	mov	bl, 0
.space:
	or	bh, 1
	jmp	.loop
.store:
	stosb
	jmp	.loop
.big:
	cmp	al, 0xB0
	jb	.normal_symbol
	cmp	al, 0xE0
	jb	.inv_symbol
	cmp	al, 0xF0
	jb	.normal_symbol
	jmp	.inv_symbol
.dot:
	test	bh, 2
	jz	.firstdot
	pop	ebx
	add	ebx, edi
	sub	ebx, ecx
	push	ebx
	cmp	ebx, ecx
	jb	@f
	pop	ebx
	push	ecx
@@:
	cmp	edi, ecx
	jbe	.skip
@@:
	dec	edi
	mov	al, [edi]
	dec	ebx
	mov	[ebx], al
	mov	byte [edi], ' '
	cmp	edi, ecx
	ja	@b
.skip:
	mov	bh, 3
	jmp	@f
.firstdot:
	cmp	bl, 8
	jz	.space
	push	edi
	or	bh, 2
@@:
	mov	edi, ecx
	mov	bl, 3
	jmp	.loop
.done:
	test	bh, 2
	jz	@f
	pop	edi
@@:
	lea	edi, [ecx-8]
	test	bh, 1
	jz	@f
	call	fat_next_short_name
@@:
	popad
	ret

;----------------------------------------------------------------
;
;  fs_RamdiskRewrite - LFN variant for writing ramdisk
;  fs_RamdiskCreateFolder - create folder on ramdisk
;
;  esi  points to file/folder name
;  ebx  ignored (reserved)
;  ecx  number of bytes to write, 0+ (ignored for folders)
;  edx  mem location to data (ignored for folders)
;
;  ret ebx = number of written bytes
;      eax = 0 ok read or other = errormsg
;
;--------------------------------------------------------------
@@:
	mov	eax, ERROR_ACCESS_DENIED
	xor	ebx, ebx
	ret

fs_RamdiskCreateFolder:
	mov	al, 1		; create folder
	jmp	fs_RamdiskRewrite.common

fs_RamdiskRewrite:
	xor	eax, eax	; create file
.common:
	cmp	byte [esi], 0
	jz	@b
	pushad
	xor	edi, edi
	push	esi
	test	ebp, ebp
	jz	@f
	mov	esi, ebp
@@:
	lodsb
	test	al, al
	jz	@f
	cmp	al, '/'
	jnz	@b
	lea	edi, [esi-1]
	jmp	@b
@@:
	pop	esi
	test	edi, edi
	jnz	.noroot
	test	ebp, ebp
	jnz	.hasebp
	push	ramdisk_root_extend_dir
	push	ramdisk_root_next_write
	push	edi
	push	ramdisk_root_first
	push	ramdisk_root_next
	jmp	.common1
.hasebp:
	mov	eax, ERROR_ACCESS_DENIED
	cmp	byte [ebp], 0
	jz	.ret1
	push	ebp
	xor	ebp, ebp
	call	rd_find_lfn
	pop	esi
	jc	.notfound0
	jmp	.common0
.noroot:
	mov	eax, ERROR_ACCESS_DENIED
	cmp	byte [edi+1], 0
	jz	.ret1
; check existence
	mov	byte [edi], 0
	push	edi
	call	rd_find_lfn
	pop	esi
	mov	byte [esi], '/'
	jnc	@f
.notfound0:
	mov	eax, ERROR_FILE_NOT_FOUND
.ret1:
	mov	[esp+28], eax
	popad
	xor	ebx, ebx
	ret
@@:
	inc	esi
.common0:
	test	byte [edi+11], 0x10	; must be directory
	mov	eax, ERROR_ACCESS_DENIED
	jz	.ret1
	movzx	ebp, word [edi+26]	; ebp=cluster
	mov	eax, ERROR_FAT_TABLE
	cmp	ebp, 2
	jb	.ret1
	cmp	ebp, 2849
	jae	.ret1
	push	ramdisk_notroot_extend_dir
	push	ramdisk_notroot_next_write
	push	ebp
	push	ramdisk_notroot_first
	push	ramdisk_notroot_next
.common1:
	call	fat_find_lfn
	jc	.notfound
; found
	test	byte [edi+11], 10h
	jz	.exists_file
; found directory; if we are creating directory, return OK,
;                  if we are creating file, say "access denied"
	add	esp, 20
	popad
	test	al, al
	mov	eax, ERROR_ACCESS_DENIED
	jz	@f
	mov	al, 0
@@:
	xor	ebx, ebx
	ret
.exists_file:
; found file; if we are creating directory, return "access denied",
;             if we are creating file, delete existing file and continue
	cmp	byte [esp+20+28], 0
	jz	@f
	add	esp, 20
	popad
	mov	eax, ERROR_ACCESS_DENIED
	xor	ebx, ebx
	ret
@@:
; delete FAT chain
	push	edi
	xor	eax, eax
	mov	dword [edi+28], eax	; zero size
	xchg	ax, word [edi+26]	; start cluster
	test	eax, eax
	jz	.done1
@@:
	cmp	eax, 0xFF8
	jae	.done1
	lea	edi, [RAMDISK_FAT + eax*2] ; position in FAT
	xor	eax, eax
	xchg	ax, [edi]
	jmp	@b
.done1:
	pop	edi
	call	get_time_for_file
	mov	[edi+22], ax
	call	get_date_for_file
	mov	[edi+24], ax
	mov	[edi+18], ax
	or	byte [edi+11], 20h	; set 'archive' attribute
	jmp	.doit
.notfound:
; file is not found; generate short name
	call	fat_name_is_legal
	jc	@f
	add	esp, 20
	popad
	mov	eax, ERROR_FILE_NOT_FOUND
	xor	ebx, ebx
	ret
@@:
	sub	esp, 12
	mov	edi, esp
	call	fat_gen_short_name
.test_short_name_loop:
	push	esi edi ecx
	mov	esi, edi
	lea	eax, [esp+12+12+8]
	mov	[eax], ebp
	call	dword [eax-4]
	jc	.found
.test_short_name_entry:
	cmp	byte [edi+11], 0xF
	jz	.test_short_name_cont
	mov	ecx, 11
	push	esi edi
	repz	cmpsb
	pop	edi esi
	jz	.short_name_found
.test_short_name_cont:
	lea	eax, [esp+12+12+8]
	call	dword [eax-8]
	jnc	.test_short_name_entry
	jmp	.found
.short_name_found:
	pop	ecx edi esi
	call	fat_next_short_name
	jnc	.test_short_name_loop
.disk_full:
	add	esp, 12+20
	popad
	mov	eax, ERROR_DISK_FULL
	xor	ebx, ebx
	ret
.found:
	pop	ecx edi esi
; now find space in directory
; we need to save LFN <=> LFN is not equal to short name <=> generated name contains '~'
	mov	al, '~'
	push	ecx edi
	mov	ecx, 8
	repnz	scasb
	push	1
	pop	eax	; 1 entry
	jnz	.notilde
; we need ceil(strlen(esi)/13) additional entries = floor((strlen(esi)+12+13)/13) total
	xor	eax, eax
@@:
	cmp	byte [esi], 0
	jz	@f
	inc	esi
	inc	eax
	jmp	@b
@@:
	sub	esi, eax
	add	eax, 12+13
	mov	ecx, 13
	push	edx
	cdq
	div	ecx
	pop	edx
.notilde:
	push	-1
	push	-1
; find <eax> successive entries in directory
	xor	ecx, ecx
	push	eax
	lea	eax, [esp+12+8+12+8]
	mov	[eax], ebp
	call	dword [eax-4]
	pop	eax
.scan_dir:
	cmp	byte [edi], 0
	jz	.free
	cmp	byte [edi], 0xE5
	jz	.free
	xor	ecx, ecx
.scan_cont:
	push	eax
	lea	eax, [esp+12+8+12+8]
	call	dword [eax-8]
	pop	eax
	jnc	.scan_dir
	push	eax
	lea	eax, [esp+12+8+12+8]
	call	dword [eax+8]		; extend directory
	pop	eax
	jnc	.scan_dir
	add	esp, 8+8+12+20
	popad
	mov	eax, ERROR_DISK_FULL
	xor	ebx, ebx
	ret
.free:
	test	ecx, ecx
	jnz	@f
	mov	[esp], edi
	mov	ecx, [esp+8+8+12+8]
	mov	[esp+4], ecx
	xor	ecx, ecx
@@:
	inc	ecx
	cmp	ecx, eax
	jb	.scan_cont
; found!
; calculate name checksum
	push	esi ecx
	mov	esi, [esp+8+8]
	mov	ecx, 11
	xor	eax, eax
@@:
	ror	al, 1
	add	al, [esi]
	inc	esi
	loop	@b
	pop	ecx esi
	pop	edi
	pop	dword [esp+8+12+8]
; edi points to last entry in free chunk
	dec	ecx
	jz	.nolfn
	push	esi
	push	eax
	mov	al, 40h
.writelfn:
	or	al, cl
	mov	esi, [esp+4]
	push	ecx
	dec	ecx
	imul	ecx, 13
	add	esi, ecx
	stosb
	mov	cl, 5
	call	.read_symbols
	mov	ax, 0xF
	stosw
	mov	al, [esp+4]
	stosb
	mov	cl, 6
	call	.read_symbols
	xor	eax, eax
	stosw
	mov	cl, 2
	call	.read_symbols
	pop	ecx
	lea	eax, [esp+8+8+12+8]
	call	dword [eax+4]	; next write
	xor	eax, eax
	loop	.writelfn
	pop	eax
	pop	esi
.nolfn:
	xchg	esi, [esp]
	mov	ecx, 11
	rep	movsb
	mov	word [edi], 20h 	; attributes
	sub	edi, 11
	pop	esi ecx
	add	esp, 12
	mov	byte [edi+13], 0	; tenths of a second at file creation time
	call	get_time_for_file
	mov	[edi+14], ax		; creation time
	mov	[edi+22], ax		; last write time
	call	get_date_for_file
	mov	[edi+16], ax		; creation date
	mov	[edi+24], ax		; last write date
	mov	[edi+18], ax		; last access date
	and	word [edi+20], 0	; high word of cluster
	and	word [edi+26], 0	; low word of cluster - to be filled
	and	dword [edi+28], 0	; file size - to be filled
	cmp	byte [esp+20+28], 0
	jz	.doit
; create directory
	mov	byte [edi+11], 10h	   ; attributes: folder
	mov	ecx, 32*2
	mov	edx, edi
.doit:
	push	edx
	push	ecx
	push	edi
	add	edi, 26 	; edi points to low word of cluster
	push	edi
	jecxz	.done
	mov	ecx, 2849
	mov	edi, RAMDISK_FAT
.write_loop:
; allocate new cluster
	xor	eax, eax
	repnz	scasw
	jnz	.disk_full2
	dec	edi
	dec	edi

    ;    lea     eax, [edi-(RAMDISK_FAT)]

	mov eax, edi
	sub eax, RAMDISK_FAT

	shr	eax, 1			; eax = cluster
	mov	word [edi], 0xFFF	; mark as last cluster
	xchg	edi, [esp]
	stosw
	pop	edi
	push	edi
	inc	ecx
; write data
	cmp	byte [esp+16+20+28], 0
	jnz	.writedir
	shl	eax, 9
	add	eax, RAMDISK+31*512
.writefile:
	mov	ebx, edx
	xchg	eax, ebx
	push	ecx
	mov	ecx, 512
	cmp	dword [esp+12], ecx
	jae	@f
	mov	ecx, [esp+12]
@@:
	call	memmove
	add	edx, ecx
	sub	[esp+12], ecx
	pop	ecx
	jnz	.write_loop
.done:
	mov	ebx, edx
	pop	edi edi ecx edx
	sub	ebx, edx
	mov	[edi+28], ebx
	add	esp, 20
	mov	[esp+16], ebx
	popad
	xor	eax, eax
	ret
.disk_full2:
	mov	ebx, edx
	pop	edi edi ecx edx
	sub	ebx, edx
	mov	[edi+28], ebx
	add	esp, 20
	mov	[esp+16], ebx
	popad
	push	ERROR_DISK_FULL
	pop	eax
	ret
.writedir:
	mov	edi, eax
	shl	edi, 9
	add	edi, RAMDISK+31*512
	mov	esi, edx
	mov	ecx, 32/4
	push	ecx
	rep	movsd
	mov	dword [edi-32], '.   '
	mov	dword [edi-32+4], '    '
	mov	dword [edi-32+8], '    '
	mov	byte [edi-32+11], 10h
	mov	word [edi-32+26], ax
	mov	esi, edx
	pop	ecx
	rep	movsd
	mov	dword [edi-32], '..  '
	mov	dword [edi-32+4], '    '
	mov	dword [edi-32+8], '    '
	mov	byte [edi-32+11], 10h
	mov	eax, [esp+16+8]
	mov	word [edi-32+26], ax
	xor	eax, eax
	mov	ecx, (512-32*2)/4
	rep	stosd
	pop	edi edi ecx edx
	add	esp, 20
	popad
	xor	eax, eax
	xor	ebx, ebx
	ret

.read_symbol:
	or	ax, -1
	test	esi, esi
	jz	.retFFFF
	lodsb
	test	al, al
	jnz	ansi2uni_char
	xor	eax, eax
	xor	esi, esi
.retFFFF:
	ret

.read_symbols:
	call	.read_symbol
	stosw
	loop	.read_symbols
	ret

;----------------------------------------------------------------
;
;  fs_RamdiskWrite - LFN variant for writing to sys floppy
;
;  esi  points to filename
;  ebx  pointer to 64-bit number = first wanted byte, 0+
;       may be ebx=0 - start from first byte
;  ecx  number of bytes to write, 0+
;  edx  mem location to data
;
;  ret ebx = bytes written (maybe 0)
;      eax = 0 ok write or other = errormsg
;
;--------------------------------------------------------------
@@:
	push	ERROR_ACCESS_DENIED
fs_RamdiskWrite.ret0:
	pop	eax
	xor	ebx, ebx
	ret

fs_RamdiskWrite:
	cmp	byte [esi], 0
	jz	@b
	pushad
	call	rd_find_lfn
	jnc	.found
	popad
	push	ERROR_FILE_NOT_FOUND
	jmp	.ret0
.found:
; must not be directory
	test	byte [edi+11], 10h
	jz	@f
	popad
	push	ERROR_ACCESS_DENIED
	jmp	.ret0
@@:
; FAT does not support files larger than 4GB
	test	ebx, ebx
	jz	.l1
	cmp	dword [ebx+4], 0
	jz	@f
.eof:
	popad
	push	ERROR_END_OF_FILE
	jmp	.ret0
@@:
	mov	ebx, [ebx]
.l1:
; now edi points to direntry, ebx=start byte to write,
; ecx=number of bytes to write, edx=data pointer
	call	fat_update_datetime

; extend file if needed
	add	ecx, ebx
	jc	.eof	; FAT does not support files larger than 4GB
	push	0	; return value=0
	cmp	ecx, [edi+28]
	jbe	.length_ok
	cmp	ecx, ebx
	jz	.length_ok
	call	ramdisk_extend_file
	jnc	.length_ok
; ramdisk_extend_file can return two error codes: FAT table error or disk full.
; First case is fatal error, in second case we may write some data
	mov	[esp], eax
	cmp	al, ERROR_DISK_FULL
	jz	.disk_full
	pop	eax
	mov	[esp+28], eax
	popad
	xor	ebx, ebx
	ret
.disk_full:
; correct number of bytes to write
	mov	ecx, [edi+28]
	cmp	ecx, ebx
	ja	.length_ok
.ret:
	pop	eax
	mov	[esp+28], eax	; eax=return value
	sub	edx, [esp+20]
	mov	[esp+16], edx	; ebx=number of written bytes
	popad
	ret
.length_ok:
; now ebx=start pos, ecx=end pos, both lie inside file
	sub	ecx, ebx
	jz	.ret
	movzx	edi, word [edi+26]	; starting cluster
.write_loop:
	sub	ebx, 0x200
	jae	.next_cluster
	push	ecx
	neg	ebx
	cmp	ecx, ebx
	jbe	@f
	mov	ecx, ebx
@@:
	mov	eax, edi
	shl	eax, 9
	add	eax, RAMDISK+31*512+0x200
	sub	eax, ebx
	mov	ebx, eax
	mov	eax, edx
	call	memmove
	xor	ebx, ebx
	add	edx, ecx
	sub	[esp], ecx
	pop	ecx
	jz	.ret
.next_cluster:
	movzx	edi, word [edi*2+RAMDISK_FAT]
	jmp	.write_loop

ramdisk_extend_file.zero_size:
	xor	eax, eax
	jmp	ramdisk_extend_file.start_extend

; extends file on ramdisk to given size, new data area is filled by 0
; in: edi->direntry, ecx=new size
; out: CF=0 => OK, eax=0
;      CF=1 => error, eax=code (ERROR_FAT_TABLE or ERROR_DISK_FULL)
ramdisk_extend_file:
	push	ecx
; find the last cluster of file
	movzx	eax, word [edi+26]	; first cluster
	mov	ecx, [edi+28]
	jecxz	.zero_size
@@:
	sub	ecx, 0x200
	jbe	@f
	mov	eax, [eax*2+RAMDISK_FAT]
	and	eax, 0xFFF
	jz	.fat_err
	cmp	eax, 0xFF8
	jb	@b
.fat_err:
	pop	ecx
	push	ERROR_FAT_TABLE
	pop	eax
	stc
	ret
@@:
	push	eax
	mov	eax, [eax*2+RAMDISK_FAT]
	and	eax, 0xFFF
	cmp	eax, 0xFF8
	pop	eax
	jb	.fat_err
; set length to full number of sectors and make sure that last sector is zero-padded
	sub	[edi+28], ecx
	push	eax edi
	mov	edi, eax
	shl	edi, 9
	lea	edi, [edi+RAMDISK+31*512+0x200+ecx]
	neg	ecx
	xor	eax, eax
	rep	stosb
	pop	edi eax
.start_extend:
	pop	ecx
; now do extend
	push	edx esi
	mov	esi, RAMDISK_FAT+2*2	   ; start scan from cluster 2
	mov	edx, 2847		; number of clusters to scan
.extend_loop:
	cmp	[edi+28], ecx
	jae	.extend_done
; add new sector
	push	ecx
	mov	ecx, edx
	push	edi
	mov	edi, esi
	jecxz	.disk_full
	push	eax
	xor	eax, eax
	repnz	scasw
	pop	eax
	jnz	.disk_full
	mov	word [edi-2], 0xFFF
	mov	esi, edi
	mov	edx, ecx
	sub	edi, RAMDISK_FAT
	shr	edi, 1
	dec	edi	; now edi=new cluster
	test	eax, eax
	jz	.first_cluster
	mov	[RAMDISK_FAT+eax*2], di
	jmp	@f
.first_cluster:
	pop	eax	; eax->direntry
	push	eax
	mov	[eax+26], di
@@:
	push	edi
	shl	edi, 9
	add	edi, RAMDISK+31*512
	xor	eax, eax
	mov	ecx, 512/4
	rep	stosd
	pop	eax	; eax=new cluster
	pop	edi	; edi->direntry
	pop	ecx	; ecx=required size
	add	dword [edi+28], 0x200
	jmp	.extend_loop
.extend_done:
	mov	[edi+28], ecx
	pop	esi edx
	xor	eax, eax	; CF=0
	ret
.disk_full:
	pop	edi ecx
	pop	esi edx
	stc
	push	ERROR_DISK_FULL
	pop	eax
	ret

fat_update_datetime:
	call	get_time_for_file
	mov	[edi+22], ax		; last write time
	call	get_date_for_file
	mov	[edi+24], ax		; last write date
	mov	[edi+18], ax		; last access date
	ret

;----------------------------------------------------------------
;
;  fs_RamdiskSetFileEnd - set end of file on ramdisk
;
;  esi  points to filename
;  ebx  points to 64-bit number = new file size
;  ecx  ignored (reserved)
;  edx  ignored (reserved)
;
;  ret eax = 0 ok or other = errormsg
;
;--------------------------------------------------------------
fs_RamdiskSetFileEnd:
	cmp	byte [esi], 0
	jnz	@f
.access_denied:
	push	ERROR_ACCESS_DENIED
	jmp	.ret
@@:
	push	edi
	call	rd_find_lfn
	jnc	@f
	pop	edi
	push	ERROR_FILE_NOT_FOUND
.ret:
	pop	eax
	ret
@@:
; must not be directory
	test	byte [edi+11], 10h
	jz	@f
	pop	edi
	jmp	.access_denied
@@:
; file size must not exceed 4Gb
	cmp	dword [ebx+4], 0
	jz	@f
	pop	edi
	push	ERROR_END_OF_FILE
	jmp	.ret
@@:
; set file modification date/time to current
	call	fat_update_datetime
	mov	eax, [ebx]
	cmp	eax, [edi+28]
	jb	.truncate
	ja	.expand
	pop	edi
	xor	eax, eax
	ret
.expand:
	push	ecx
	mov	ecx, eax
	call	ramdisk_extend_file
	pop	ecx
	pop	edi
	ret
.truncate:
	mov	[edi+28], eax
	push	ecx
	movzx	ecx, word [edi+26]
	test	eax, eax
	jz	.zero_size
; find new last sector
@@:
	sub	eax, 0x200
	jbe	@f
	movzx	ecx, word [RAMDISK_FAT+ecx*2]
	jmp	@b
@@:
; zero data at the end of last sector
	push	ecx
	mov	edi, ecx
	shl	edi, 9
	lea	edi, [edi+RAMDISK+31*512+eax+0x200]
	mov	ecx, eax
	neg	ecx
	xor	eax, eax
	rep	stosb
	pop	ecx
; terminate FAT chain
	lea	ecx, [RAMDISK_FAT+ecx+ecx]
	push	dword [ecx]
	mov	word [ecx], 0xFFF
	pop	ecx
	and	ecx, 0xFFF
	jmp	.delete
.zero_size:
	and	word [edi+26], 0
.delete:
; delete FAT chain starting with ecx
; mark all clusters as free
	cmp	ecx, 0xFF8
	jae	.deleted
	lea	ecx, [RAMDISK_FAT+ecx+ecx]
	push	dword [ecx]
	and	word [ecx], 0
	pop	ecx
	and	ecx, 0xFFF
	jmp	.delete
.deleted:
	pop	ecx
	pop	edi
	xor	eax, eax
	ret

fs_RamdiskGetFileInfo:
	cmp	byte [esi], 0
	jnz	@f
	mov	eax, 2	; unsupported
	ret
@@:
	push	edi
	call	rd_find_lfn
fs_GetFileInfo_finish:
	jnc	@f
	pop	edi
	mov	eax, ERROR_FILE_NOT_FOUND
	ret
@@:
	push	esi ebp
	xor	ebp, ebp
	mov	esi, edx
	and	dword [esi+4], 0
	call	fat_entry_to_bdfe2
	pop	ebp esi
	pop	edi
	xor	eax, eax
	ret

fs_RamdiskSetFileInfo:
	cmp	byte [esi], 0
	jnz	@f
	mov	eax, 2	; unsupported
	ret
@@:
	push	edi
	call	rd_find_lfn
	jnc	@f
	pop	edi
	mov	eax, ERROR_FILE_NOT_FOUND
	ret
@@:
	call	bdfe_to_fat_entry
	pop	edi
	xor	eax, eax
	ret

;----------------------------------------------------------------
;
;  fs_RamdiskDelete - delete file or empty folder from ramdisk
;
;  esi  points to filename
;
;  ret  eax = 0 ok or other = errormsg
;
;--------------------------------------------------------------
fs_RamdiskDelete:
	cmp	byte [esi], 0
	jnz	@f
; cannot delete root!
.access_denied:
	push	ERROR_ACCESS_DENIED
.pop_ret:
	pop	eax
	ret
@@:
	and	[rd_prev_sector], 0
	and	[rd_prev_prev_sector], 0
	push	edi
	call	rd_find_lfn
	jnc	.found
	pop	edi
	push	ERROR_FILE_NOT_FOUND
	jmp	.pop_ret
.found:
	cmp	dword [edi], '.   '
	jz	.access_denied2
	cmp	dword [edi], '..  '
	jz	.access_denied2
	test	byte [edi+11], 10h
	jz	.dodel
; we can delete only empty folders!
	movzx	eax, word [edi+26]
	push	ebx
	mov	ebx, eax
	shl	ebx, 9
	add	ebx, RAMDISK + 31*0x200 + 2*0x20
.checkempty:
	cmp	byte [ebx], 0
	jz	.empty
	cmp	byte [ebx], 0xE5
	jnz	.notempty
	add	ebx, 0x20
	test	ebx, 0x1FF
	jnz	.checkempty
	movzx	eax, word [RAMDISK_FAT + eax*2]
	test	eax, eax
	jz	.empty
	mov	ebx, eax
	shl	ebx, 9
	add	ebx, RAMDISK + 31*0x200
	jmp	.checkempty
.notempty:
	pop	ebx
.access_denied2:
	pop	edi
	jmp	.access_denied
.empty:
	pop	ebx
.dodel:
	movzx	eax, word [edi+26]
; delete folder entry
	mov	byte [edi], 0xE5
; delete LFN (if present)
.lfndel:
	test	edi, 0x1FF
	jnz	@f
	cmp	[rd_prev_sector], 0
	jz	@f
	cmp	[rd_prev_sector], -1
	jz	.lfndone
	mov	edi, [rd_prev_sector]
	push	[rd_prev_prev_sector]
	pop	[rd_prev_sector]
	or	[rd_prev_prev_sector], -1
	shl	edi, 9
	add	edi, RAMDISK + 31*0x200 + 0x200
@@:
	sub	edi, 0x20
	cmp	byte [edi], 0xE5
	jz	.lfndone
	cmp	byte [edi+11], 0xF
	jnz	.lfndone
	mov	byte [edi], 0xE5
	jmp	.lfndel
.lfndone:
; delete FAT chain
	test	eax, eax
	jz	.done
	lea	eax, [RAMDISK_FAT + eax*2]
	push	dword [eax]
	and	word [eax], 0
	pop	eax
	and	eax, 0xFFF
	jmp	.lfndone
.done:
	pop	edi
	xor	eax, eax
	ret

; \end{diamond}