;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2011. 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, 0xF0 ; 'Ё'
        jmp     .doit
.yo2:
        mov     al, 0xF1 ; 'ё'
        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, 0xF0 ; 'Ё'
        jz      .yo1
        cmp     al, 0xF1 ; 'ё'
        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, 0xF1 ; 'ё'
        jz      .yo1
        cmp     al, 0xA0 ; 'а'
        jb      .ret
        cmp     al, 0xE0 ; 'р'
        jb      .rus1
        cmp     al, 0xEF ; 'я'
        ja      .ret
; 0xE0-0xEF -> 0x90-0x9F
        sub     al, 0xE0-0x90
.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
        movi    ebx, 8
        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
        movi    eax, 1     ; 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!
; If creating a directory, allocate one data cluster now and fail immediately
; if this is impossible. This prevents from creating an invalid directory entry
; on a full disk.
; yup, the argument is quite non-intuitive... but what should I do if
; the entire function uses such arguments? BTW, it refers to al from pushad,
; which in turn is filled with 0 in fs_RamdiskRewrite and 1 in fs_RamdiskCreateFolder.
        push    esi ecx
        cmp     byte [esp+24+12+20+28], 0
        jz      .no.preallocate.folder.data
        mov     ecx, 2849
        mov     edi, RAMDISK_FAT
        xor     eax, eax
        repnz scasw
        jz      @f
        add     esp, 24
        jmp     .disk_full
@@:
        mov     [esp+24+12+20+20], edi  ; store the cluster somewhere
.no.preallocate.folder.data:
; calculate name checksum
        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
        push    edx
        push    ecx
        push    edi
        add     edi, 26         ; edi points to low word of cluster
        push    edi
        mov     edi, [esp+16+20+20]
        jmp     .doit2
.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
.doit2:
        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
        movi    eax, ERROR_DISK_FULL
        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
        movi    eax, ERROR_FAT_TABLE
        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
        movi    eax, ERROR_DISK_FULL
        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
        cmp     eax, 2
        jb      .done
        cmp     eax, 0xFF8
        jae     .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}