        TITLE   'SFT - Support SFT Management'
        PAGE 59, 132
        .LALL

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Support SFT Management                                       ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Real Time Dos                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This material  was created as a published version  of a DOS  ;
        ;  equivalent product.   This program  logically  functions in  ;
        ;  the same way as  MSDOS functions and it  is  internal  data  ;
        ;  structure compliant with MSDOS 6.0                           ;
        ;                                                               ;
        ;  This product is distributed  AS IS and contains no warranty  ;
        ;  whatsoever,   including  warranty  of   merchantability  or  ;
        ;  fitness for a particular purpose.                            ;
        ;                                                               ;
        ;                                                               ;
        ;  (c) Copyright 1990, 1997. Api Software and Mike Podanoffsky  ;
        ;      All Rights Reserved Worldwide.                           ;
        ;                                                               ;
        ;  This product is protected under copyright laws and  may not  ;
        ;  be reproduced  in whole  or in part, in any form  or media,  ;
        ;  included but not limited to source listing, facsimile, data  ;
        ;  transmission, cd-rom, or  floppy disk without the expressed  ;
        ;  written consent of the author.                               ;
        ;                                                               ;
        ;  License  for  distribution for  commercial  use  or  resale  ;
        ;  required from:                                               ;
        ;                                                               ;
        ;  Api Software                                                 ;
        ;  12 South Walker Street                                       ;
        ;  Lowell,  MA   01851                                          ;
        ;                                                               ;
        ;  internet: mikep@world.std.com                                ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;  Compile with MASM 5.1                                        ;
        ;...............................................................;

        include rxdosmac.asm
        include rxdosdef.asm

RxDOS   SEGMENT PARA PUBLIC 'CODE'
        assume cs:RxDOS, ds:RxDOS, es:RxDOS, ss:RxDOS

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Handle/ SFT File System Functions                            ;
        ;...............................................................;

        public createSFTEntry
        public FindAvailableSFTHandle
        public findmatchingFCBSFT
        public FindSFTbyHandle
        public InitSFTEntry
        public initSFTfromDirEntry
        public MapApptoSFTHandle
        public MapSFTtoAppHandle 
        public releaseSFT
        public VerifyAvailableHandle
        public TestIfMoveHandleTable

        public _SFTCloseAllFiles
        public _SFTCloseFile
        public _SFTCommitFile
        public _SFTCreateFile
        public _SFTOpenFile
        public _SFTReadFile
        public _SFTWriteFile
        public _SFTReadLine
        public invalidateOpenSFTs

        extrn AllocateInitCluster               : near
        extrn CCBChanged                        : near
        extrn convFilenametoFCBString           : near
        extrn devCharRead                       : near
        extrn devCharWrite                      : near
        extrn devCharReadLine                   : near
        extrn devCharWriteLine                  : near

        extrn fillLogicalBuffer                 : near
        extrn getDPB                            : near
        extrn getAddrDPB                        : near
        extrn getNextCluster                    : near
        extrn getSysDateinDirFormat             : near
        extrn initdiskAccess                    : near
        extrn locateCCBPHeader                  : near
        extrn LocateFile                        : near
        extrn LocateFreeDirSlot                 : near
        extrn readBuffer                        : near
        extrn readLine                          : near
        extrn readLogicalBuffer                 : near
        extrn ReleaseClusterChain               : near
        extrn AllocateCluster                   : near

        extrn incorrectDiskMedia                : near
        extrn updateChangedCCB                  : near
        extrn updateAllChangedCCBBuffers        : near
        extrn writeLogicalBuffer                : near
        extrn _div32                            : near
        extrn _RxDOS_CurrentPSP                 : word
        extrn _RxDOS_TabPosition                : word
        extrn _RxDOS_pFT                        : dword

        extrn pexterrInvalidFunction            : near
        extrn pexterrFileNotFound               : near
        extrn pexterrPathNotFound               : near
        extrn pexterrIllegalName                : near
        extrn pexterrNoHandlesAvailable         : near
        extrn pexterrAccessDenied               : near
        extrn pexterrInvalidHandle              : near
        extrn pexterrArenaTrashed               : near
        extrn pexterrNotEnoughMemory            : near
        extrn pexterrInvalidBlock               : near
        extrn pexterrInvalidAccess              : near
        extrn pexterrInvalidDrive               : near
        extrn pexterrCurrentDirectory           : near
        extrn pexterrNoMoreFiles                : near
        extrn pexterrFileExists                 : near

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Map handle found to App handle                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;  ax   handle id #                                             ;
        ;...............................................................;

MapSFTToAppHandle:
        cmp word ptr ss:[ _RxDOS_CurrentPSP ], 0000
        jz MapSFTToAppHandle_16                         ; if no app -->

        push es
        push si
        push cx

        mov es, word ptr ss:[ _RxDOS_CurrentPSP ]       ; get PSP
        mov cx, word ptr es:[ pspFileHandleCount ]      ; get max count
        les si, dword ptr es:[ pspFileHandlePtr  ]      ; get JHT pointer

MapSFTToAppHandle_04:
        cmp byte ptr es:[ si ], -1                      ; empty slot ?
        jz MapSFTToAppHandle_08                         ; yes, allocate -->

        inc si                                          ; next
        loop MapSFTToAppHandle_04                       ; continue looping

        stc
        mov ax, offset pexterrNoHandlesAvailable        ; problem, no app handles left
        jmp short MapSFTToAppHandle_12                  ; exit -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  allocate App Handle
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

MapSFTToAppHandle_08:
        mov byte ptr es:[ si ], al                      ; replace sys handle into app
        mov ax, si                                      ; app handle offset

        mov es, word ptr ss:[ _RxDOS_CurrentPSP ]       ; restore PSP address
        sub ax, word ptr es:[ pspFileHandlePtr. _pointer ]

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

MapSFTToAppHandle_12:
        pop cx
        pop si
        pop es

MapSFTToAppHandle_16:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Map App Handle to SFT Handle                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;  ax   SFT handle id #                                         ;
        ;...............................................................;

MapAppToSFTHandle:
        push es
        push si
        cmp word ptr ss:[ _RxDOS_CurrentPSP ], 0000
        jz MapAppToSFTHandle_08                         ; if no app -->

        mov si, ax
        mov ax, 00FFh                                   ; assume can't be found

        mov es, word ptr ss:[ _RxDOS_CurrentPSP ]       ; get PSP
        cmp si, word ptr es:[ pspFileHandleCount ]      ; beyond max handles
        jnc MapAppToSFTHandle_08                        ; yes, con't convert -->
        les ax, dword ptr es:[ pspFileHandlePtr ]       ; get JHT address
        add si, ax                                      ; use file handle as offset
        mov al, byte ptr es:[ si ]                      ; get SFT handle
        mov ah, 0

MapAppToSFTHandle_08:
        pop si
        pop es
        cmp ax, 00FFh                                   ; see if its unused
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Find Available File Handle                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ; File handles are stored in a dynamic file handles area created;
        ; on initialization by the FILES = < arg > command.             ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ; Returns:                                                      ;
        ;  ax   handle id #                                             ;
        ; es:bx pointer to available SFT entry                          ;
        ;                                                               ;
        ; SFT   entry is automatically reserved and must be cleared     ;
        ;       if it wont be used.                                     ;
        ;...............................................................;

FindAvailableSFTHandle:

        Entry
        ddef _currentFT
        def  _entries
        def  _handle, 0000

        les bx, dword ptr ss:[ _RxDOS_pFT ]

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  look for available handle
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

findSFTHandle_08:
        stordarg _currentFT, es, bx

        mov cx, word ptr es:[ numberSFTEntries ][ bx ]  ; get # files at this sft
        mov word ptr [ _entries ][ bp ], cx

        lea bx, offset sizeFT [ bx ]
        or cx, cx 
        jle findSFTHandle_22

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is handle available ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

findSFTHandle_12:
        cli
        cmp word ptr es:[ sftRefCount ][ bx ], 0        ; entry available ?
        jnz findSFTHandle_14                            ; no -->
        inc word ptr es:[ sftRefCount ][ bx ]           ; RESERVE ENTRY
        mov ax, word ptr [ _handle ][ bp ]              ; count handles
        jmp short findSFTHandle_30                      ; exit -->

findSFTHandle_14:
        sti
        inc word ptr [ _handle ][ bp ]
        add bx, sizeSFT                                 ; next SFT entry
        loop findSFTHandle_12                           ; if more -->

        les bx, dword ptr [ _currentFT ][ bp ]
        cmp word ptr es:[ nextFTPointer. _pointer ][ bx ], -1
        jz findSFTHandle_22

        les bx, dword ptr es:[ nextFTPointer ][ bx ]
        jmp findSFTHandle_08                            ; go to next FT -->
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if out of handles system wide
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

findSFTHandle_22:
        stc
        mov ax, offset pexterrNoHandlesAvailable

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

findSFTHandle_30:
        sti
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Find SFT by Handle                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     handle id #                                          ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:di  pointer to matching SFT entry                        ;
        ;...............................................................;

FindSFTbyHandle:

        push ax
        push es
        les di, dword ptr ss:[ _RxDOS_pFT ]
        cmp ax, 00FFh                                   ; if not assigned
        jnc FindSFTbyHandle_12                          ; if greater than or equal -->

FindSFTbyHandle_08:
        cmp ax, word ptr es:[ numberSFTEntries ][ di ]
        jc FindSFTbyHandle_16                           ; if within this block -->

        sub ax, word ptr es:[ numberSFTEntries ][ di ]
        cmp word ptr es:[ nextFTPointer. _pointer ][ di ], -1
        jz FindSFTbyHandle_12
        les di, dword ptr es:[ nextFTPointer ][ di ]
        jmp FindSFTbyHandle_08

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FindSFTbyHandle_12:
        mov ax, offset pexterrInvalidHandle             ; set illegal handle error
        pop es                                          ; restore es:
        pop ax                                          ; restore ax
        stc
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if located handle space
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FindSFTbyHandle_16:
        push cx
        mov cx, sizeSFT
        mul cl                                          ; find offset
        add di, sizeFT                                  ; point past header
        add di, ax                                      ; plus offset

        pop cx                                          ; restore cx
        pop ax                                          ; throw away es:
        pop ax                                          ; restore ax (handle)
        clc
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Release SFT                                                  ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  pointer to SFT entry                                 ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:di  pointer to matching SFT entry                        ;
        ;...............................................................;

releaseSFT:

        cmp word ptr es:[ sftRefCount ][ di ], 0000
        jz releaseSFT_08                                ; already released -->
        dec word ptr es:[ sftRefCount ][ di ]           ; clear entry count

releaseSFT_08:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Find Matching FCB SFT                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  FCB address                                          ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:di  pointer to matching SFT entry                        ;
        ;...............................................................;

findmatchingFCBSFT:

        Entry
        ddef _fcbaddress, es, di                        ; fcb pointer
        ddef _sftPointer                                ; sft pointer
        def  _sftHandle                                 ; sft handle

        xor ax, ax
        mov dx, word ptr es:[ fcbBegClusterNo ][ di ]
        mov al, byte ptr es:[ fcbSFN          ][ di ]

        call FindSFTbyHandle                            ; locate matching SFT
        jc findmatchingFCBSFT_08                        ; if cannot locate SFT -->

        cmp dx, word ptr es:[ sftBegCluster ][ di ]
        ifz findmatchingFCBSFT_26                       ; if entry matches -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if not found, try to allocate new SFT
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

findmatchingFCBSFT_08:
        call FindAvailableSFTHandle
        ifc findmatchingFCBSFT_26                       ; if can find an entry -->

        storarg  _sftHandle, ax                         ; sft handle
        stordarg _sftPointer, es, bx                    ; sft pointer

        push ds

    ; clear SFT

        xor ax, ax
        mov di, bx                                      ; pointer to SFT
        add di, 2                                       ; skip Count entry
        mov cx, (sizeSFT - sftRefCount - 2)/ 2          ; effective count
        rep stosw                                       ; must have pointer at es: di

        getdarg ds, si, _fcbAddress                     ; fcb address
        getdarg es, di, _sftPointer                     ; sft pointer
        or word ptr es:[ sftMode ][ di ], sftENTRY_ISFCB

    ; set owner PSP

        mov ax, word ptr ss:[ _RxDOS_CurrentPSP ]       ; get owner PSP
        mov word ptr es:[ sftOwnerPSP ][ di ], ax

    ; set drive

        mov cl, byte ptr [ fcbDrive ][ si ]             ; get drive
        and cx, sftDrivemask
        mov word ptr es:[ sftDevInfo ][ di ], cx        ; include drive 

        push word ptr [ fcbTime ][ si ]                 ; time last updated
        push word ptr [ fcbDate ][ si ]                 ; date last updated
        push word ptr [ fcbBegClusterNo ][ si ]         ; beginning cluster
        push word ptr [ fcbFileSize. _low  ][ si ]      ; file size
        push word ptr [ fcbFileSize. _high ][ si ]      ; file size
        push word ptr [ fcbDHD. _pointer ][ si ]        ; device header
        push word ptr [ fcbDHD. _segment ][ si ]        ; 

        pop word ptr es:[ sftDCB. _segment ][ di ]
        pop word ptr es:[ sftDCB. _pointer ][ di ]
        pop word ptr es:[ sftFileSize. _high ][ di ]
        pop word ptr es:[ sftFileSize. _low  ][ di ]
        pop word ptr es:[ sftBegCluster ][ di ]
        pop word ptr es:[ sftDate ][ di ]
        pop word ptr es:[ sftTime ][ di ]

        lea di, offset sftFileName [ di ]               ; offset to filename
        lea si, offset fcbName [ si ]                   ; offset to filename
        mov cx, sizeFILENAME                            ; length of name/ ext
        rep movsb                                       ; copy name

        pop ds

        getdarg es, di, _fcbAddress                     ; fcb address
        getarg  ax, _sftHandle                          ; sft handle
        mov byte ptr es:[ fcbSFN ][ di ], al

        getdarg es, di, _sftPointer                     ; sft pointer
        clc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

findmatchingFCBSFT_26:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Invalidate SFTs belonging to changed drive                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   cx:dx  Old Serial Number                                    ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   nz     if open sfts exist                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ; NO LONGER USED                                                ;
        ;...............................................................;

invalidateOpenSFTs:

        Entry
        ddef _currentFT
        ddef _PrevSerialNumber, cx, dx
        def  _drive, ax

        push es
        push bx
        les bx, dword ptr ss:[ _RxDOS_pFT ]

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  look for available handle
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

invOpenSFT_08:
        stordarg _currentFT, es, bx

        mov cx, word ptr es:[ numberSFTEntries ][ bx ]  ; get # files at this sft
        lea bx, offset sizeFT [ bx ]
        or cx, cx                                       ; none ?
        jle invOpenSFT_16                               ; go to next -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is SFT available ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

invOpenSFT_12:
        cmp word ptr es:[ sftRefCount ][ bx ], 0        ; entry in use ?
        jz invOpenSFT_14                                ; no, skip to next -->

        mov dx, word ptr es:[ sftDevInfo ][ bx ]
        test dx, sftIsDevice                            ; is device ?
        jnz invOpenSFT_14                               ; no, skip to next -->

        and dx, sftDrivemask
        cmp dx, ax                                      ; same as changed drive ?
        jnz invOpenSFT_14                               ; no -->

        cmp ax, -1                                      ; not zero flag
        jmp short invOpenSFT_26                         ; we have a problem -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  goto next sft
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

invOpenSFT_14:
        add bx, sizeSFT                                 ; next SFT entry
        loop invOpenSFT_12                              ; if more -->

invOpenSFT_16:
        les bx, dword ptr [ _currentFT ][ bp ]
        cmp word ptr es:[ nextFTPointer. _pointer ][ bx ], -1
        jz invOpenSFT_26

        les bx, dword ptr es:[ nextFTPointer ][ bx ]
        jmp invOpenSFT_08                            ; go to next FT -->
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

invOpenSFT_26:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Init Clear SFT Entry                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ; es:bx pointer to available SFT entry                          ;
        ;...............................................................;

InitSFTEntry:

        push di
        push cx
        push ax

        xor ax, ax
        push word ptr es:[ sftRefCount ][ bx ]

        mov di, bx
        mov cx, sizeSFT / 2
        rep stosw                                       ; must have pointer at es: di

        mov ax, word ptr ss:[ _RxDOS_CurrentPSP ]       ; get owner PSP
        mov word ptr es:[ sftOwnerPSP ][ bx ], ax
        pop word ptr es:[ sftRefCount ][ bx ]

        pop ax
        pop cx
        pop di
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Verify SFT/ JHT Available                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Returns:                                                     ;
        ;  ax   handle id #                                             ;
        ;...............................................................;

VerifyAvailableHandle:

        cmp word ptr ss:[ _RxDOS_CurrentPSP ], 0000
        jz verifyAvailHandles_26                        ; if no app -->

        push es
        push si
        push cx

        mov es, word ptr ss:[ _RxDOS_CurrentPSP ]       ; get PSP
        mov cx, word ptr es:[ pspFileHandleCount ]
        les si, dword ptr es:[ pspFileHandlePtr  ]

verifyAvailHandles_04:
        cmp byte ptr es:[ si ], -1                      ; empty slot ?
        jz verifyAvailHandles_08                        ; yes, allocate -->

        inc si                                          ; next
        loop verifyAvailHandles_04                      ; continue looping

        stc
        mov ax, offset pexterrNoHandlesAvailable        ; problem, no app handles left
        jmp short verifyAvailHandles_22                 ; exit -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  allocate App Handle
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

verifyAvailHandles_08:
        call FindAvailableSFTHandle                     ; locate avail System Handle
        jc verifyAvailHandles_22                        ; if none left -->
        dec word ptr es:[ sftRefCount ][ bx ]           ; release reservation count

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

verifyAvailHandles_22:
        pop cx
        pop si
        pop es

verifyAvailHandles_26:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Build Directory /SFT Entries                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  pointer to diraccess                                 ;
        ;   cx     attributes                                           ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     handle to an SFT                                     ;
        ;...............................................................;

createSFTEntry:

        Entry
        def  _drive
        def  _handle, -1                                ; SFT handle
        def  _DirEntryAttributes, cx                    ; directory attributes
        ddef _dirAccess, ss, di
        ddef _dirAddress                                ; address of empty loc found
        ddef _sftPointer                                ; sft pointer

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  find an available SFT
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        call FindAvailableSFTHandle                     ; find an available file handle
        ifc _createSFTEntry_42                          ; if none available -->

        mov word ptr [ _handle ][ bp ], ax              ; handle
        stordarg _sftPointer, es, bx                    ; sft pointer
        call InitSFTEntry                               ; clear entry

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if file exists, delete file by releasing its clusters
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov si, word ptr [ _dirAccess. _pointer ][ bp ]

        mov ax, word ptr ss:[ fileAcDrive ][ si ]
        storarg _drive, ax                              ; save drive
        test ax, 8000h                                  ; does not exist flag ?
        jnz _createSFTEntry_20                          ; if no file -->

        mov si, word ptr [ _dirAccess. _pointer ][ bp ]
        les di, dword ptr ss:[ fileAcBufferPtr ][ si ]
        add di, word ptr ss:[ fileAcDirOffset ][ si ]   ; offset to entry in dir
        stordarg _dirAddress, es, di                    ; address of empty loc found

        mov dx, word ptr ss:[ fileAcCluster ][ si ]
        mov word ptr ss:[ fileAcCluster ][ si ], 0000
        call ReleaseClusterChain
        jmp short _createSFTEntry_24

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if no file, locate empty slot and populate
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_createSFTEntry_20:
        and ax, 7FFFh
        mov word ptr ss:[ fileAcDrive   ][ si ], ax
        mov dx, word ptr ss:[ fileAcDirCluster ][ si ]
        call LocateFreeDirSlot                          ; can we find a valid empty entry ?
        jc _createSFTEntry_40                           ; no -->

        stordarg _dirAddress, es, si                    ; address of empty loc found
        mov di, si                                      ; convert name to dir style

        mov si, word ptr [ _dirAccess. _pointer ][ bp ]
        mov si, word ptr ss:[ fileAcNameOffset ][ si ]
        call convFilenametoFCBString                    ; dir style name [si]-->[di]

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  update latest entry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_createSFTEntry_24:
        xor ax, ax
        mov cx, sizeDIRENTRY - sizeFILENAME             ; remainder of dir entry
        getdarg es, di, _dirAddress                     ; restore di pointer
        lea di, deAttributes [ di ]                     ; starting at attribs
        rep stosb                                       ; clear rest of entry

        mov si, word ptr [ _dirAddress. _pointer ][ bp ]
        mov cx, word ptr [ _DirEntryAttributes ][ bp ]  ; get attributes
        mov byte ptr es:[ deAttributes ][ si ], cl      ; attribute byte

        call getSysDateinDirFormat
        mov word ptr es:[ deTime ][ si ], ax            ; time created
        mov word ptr es:[ deDate ][ si ], dx            ; date created

        mov word ptr es:[ deStartCluster    ][ si ], 0000
        mov word ptr es:[ deFileSize. _low  ][ si ], 0000
        mov word ptr es:[ deFileSize. _high ][ si ], 0000

        call locateCCBPHeader                           ; get ccb header into es:di
        call CCBChanged                                 ; mark changes made
        call updateChangedCCB                           ; updated buffer to disk

        mov ax, offset errAccessDenied                  ; problem, access denied
        jc _createSFTEntry_40                           ; if error, release SFT -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  fix-up SFT
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        push word ptr [ _dirAddress. _segment ][ bp ]
        push word ptr [ _dirAddress. _pointer ][ bp ]   ; dir entry
        push word ptr [ _sftPointer. _segment ][ bp ]
        push word ptr [ _sftPointer. _pointer ][ bp ]   ; sft pointer
        call initSFTfromDirEntry

        getarg ax, _handle
        cmp word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        clc
        jnz _createSFTEntry_42

        or word ptr es:[ sftDevInfo ][ di ], sftWritten ; is written when created
        jmp short _createSFTEntry_42

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if error, release locked sft
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_createSFTEntry_40:
        getdarg es, bx, _sftPointer                     ; get sft pointer
        dec word ptr es:[ sftRefCount ][ bx ]           ; release completely
        stc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_createSFTEntry_42:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Build Directory /SFT Entries                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   stack  pointer to directory entry                           ;
        ;   stack  sft pointer                                          ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:di  sft pointer                                          ;
        ;...............................................................;

initSFTfromDirEntry:

        Entry 4
        darg _dirAddress           
        darg _sftPointer                                ; sft pointer

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init SFT
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        push ds
        xor ax, ax
        getdarg es, di, _sftPointer                     ; sft pointer
        add di, 2                                       ; skip Count entry
        mov cx, (sizeSFT - sftRefCount - 2)/ 2          ; effective count
        rep stosw                                       ; must have pointer at es: di

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  copy filename
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getdarg ds, si, _dirAddress                     ; directory
        getdarg es, di, _sftPointer                     ; sft pointer
        lea di, offset sftFileName [ di ]               ; offset to filename
        mov cx, sizeFILENAME                            ; length of name/ ext
        rep movsb                                       ; copy name

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  populate sft
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getdarg ds, si, _dirAddress                     ; directory
        getdarg es, di, _sftPointer                     ; sft pointer

        push word ptr [ deAttributes ][ si ]            ; attribute byte
        push word ptr [ deTime ][ si ]                  ; time last updated
        push word ptr [ deDate ][ si ]                  ; date last updated
        push word ptr [ deStartCluster ][ si ]          ; beginning cluster
        push word ptr [ deFilesize. _low  ][ si ]       ; file size
        push word ptr [ deFilesize. _high ][ si ]       ; file size

        pop word ptr es:[ sftFileSize. _high ][ di ]
        pop word ptr es:[ sftFileSize. _low  ][ di ]
        pop word ptr es:[ sftBegCluster ][ di ]
        pop word ptr es:[ sftDate ][ di ]
        pop word ptr es:[ sftTime ][ di ]
        pop ax
        mov byte ptr es:[ sftFileAttrib ][ di ], al

    ; set owner PSP

        mov ax, word ptr ss:[ _RxDOS_CurrentPSP ]       ; get owner PSP
        mov word ptr es:[ sftOwnerPSP ][ di ], ax

    ; set where in directory

        push es
        push di                                         ; pointer to sft
        setES ds                                        ; point dir buffer as [es][si]
        call locateCCBPHeader                           ; get ccb header into es:di
        mov si, di                                      ; ccb Header at [ds:si]

        pop di
        pop es                                          ; pointer to sft
        mov ax, word ptr [ ccbLBN. _low       ][ si ]
        mov dx, word ptr [ ccbLBN. _high      ][ si ]
        mov word ptr es:[ sftDirSector. _low  ][ di ], ax
        mov word ptr es:[ sftDirSector. _high ][ di ], dx

        mov cl, byte ptr [ ccbDrive ][ si ]             ; get drive
        and cx, sftDrivemask
        or word ptr es:[ sftDevInfo ][ di ], cx         ; include drive 

        mov ax, word ptr [ _dirAddress ][ bp ]
        sub ax, si                                      ; distance from start of ccb header
        sub ax, ccbData                                 ; distance from start of dir sector
        mov cl, sizeDIRENTRY                            ; bytes per entry
        div cl                                          ; remainder should be zero

        mov byte ptr es:[ sftDirIndex ][ di ], al

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  record disk drive volume id
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, word ptr es:[ sftDevInfo ][ di ]        ; drive 
        and ax, sftDrivemask
        call getDPB                                     ; get DPB address

        mov cx, word ptr es:[ _dpbVolumeId. _High ][ bx ]
        mov dx, word ptr es:[ _dpbVolumeId. _Low  ][ bx ]

        getdarg es, di, _sftPointer                     ; sft pointer
        mov word ptr es:[ sftVolumeId. _High ][ di ], cx
        mov word ptr es:[ sftVolumeId. _Low  ][ di ], dx
        clc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        pop ds
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Build Access Area from SFT                                   ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  SFT entry                                            ;
        ;   ss:bx  access block                                         ;
        ;...............................................................;

buildAccessRef:

        Entry
        ddef _fileposAtBegCluster
        def  _clusterSize

        saveRegisters si, cx

        push es
        push di
        push bx

        mov ax, word ptr es:[ sftDevInfo    ][ di ]
        and ax, sftDrivemask
        push ax
        mov dx, word ptr es:[ sftBegCluster ][ di ]
        call initdiskAccess                             ; [ax] is drive, [dx] is cluster

        pop ax                                          ; restore drive
        call getDPB                                     ; gets a guaranteed init drive block

        mov ax, word ptr es:[ _dpbBytesPerSector ][ bx ]
        mov cl, byte ptr es:[ _dpbClusterSizeShift ][ bx ]
        shl ax, cl                                      ; bytes per cluster
        storarg _clusterSize, ax

        pop bx
        pop di
        pop es

        mov cx, word ptr es:[ sftRelCluster ][ di ]
        or cx, cx                                       ; cluster zeroed out ?
        jz buildAccessRef_08                            ; can't use curr cluster -->

        xor dx, dx
        mul cx                                          ; file position at start of cluster
        stordarg _fileposAtBegCluster, dx, ax           ; save

        mov ax, word ptr es:[ sftFilePosition. _low  ][ di ]
        mov dx, word ptr es:[ sftFilePosition. _high ][ di ]
        sub ax, word ptr [ _fileposAtBegCluster. _low  ][ bp ]
        sbb dx, word ptr [ _fileposAtBegCluster. _high ][ bp ]
        jc buildAccessRef_08                            ; can't use curr cluster -->
        or dx, dx                                       ; distance greater than 65k ?
        jnz buildAccessRef_08                           ; can't use curr cluster -->

        cmp ax, word ptr [ _clusterSize ][ bp ]         ; greater than cluster size ?
        jge buildAccessRef_08                           ; can't use curr cluster -->

   ; there's a bug here, so we'll just ignore it for now
   ; by ALWAYS exiting.  This patch could be a problem
   ; for larger files, so it needs to be fixed !

     jmp buildAccessRef_08                           ; can't use curr cluster -->
     

        mov dx, word ptr es:[ sftCurCluster ][ di ]
        mov word ptr ss:[ diskAcCurCluster ][ bx ], dx  ; ok, within cluster 

        getdarg dx, ax, _fileposAtBegCluster            ; offset at start of cluster
        mov word ptr ss:[ diskAcOffAtBegBuffer. _low  ][ bx ], ax
        mov word ptr ss:[ diskAcOffAtBegBuffer. _high ][ bx ], dx
        mov word ptr ss:[ diskAcOffAtBegCluster. _low  ][ bx ], ax
        mov word ptr ss:[ diskAcOffAtBegCluster. _high ][ bx ], dx

buildAccessRef_08:
        restoreRegisters cx, si
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Set Relative Cluster Number From SFT FilePosition            ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  SFT entry                                            ;
        ;                                                               ;
        ;  Preserved:                                                   ;
        ;   CX, ES, DI                                                  ;
        ;...............................................................;

_computeRelativeCluster:

        push cx
        push es
        push di

        mov ax, word ptr es:[ sftDevInfo    ][ di ]
        and ax, sftDrivemask
        call getAddrDPB                                 ; es:bx

        mov ax, word ptr es:[ _dpbBytesPerSector ][ bx ]
        mov cl, byte ptr es:[ _dpbClusterSizeShift ][ bx ]
        shl ax, cl                                      ; bytes per cluster
        mov cx, ax

        pop di
        pop es
        mov ax, word ptr es:[ sftFilePosition. _low  ][ di ]
        mov dx, word ptr es:[ sftFilePosition. _high ][ di ]
        call _div32

        mov word ptr es:[ sftRelCluster ][ di ], ax
        pop cx
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Open SFT                                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:dx  unexpanded filename                                  ;
        ;   ax     open mode                                            ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:di  sft pointer                                          ;
        ;   zr     means end of file or wrong address                   ;
        ;...............................................................;

_SFTOpenFile:

        Entry
        def  _handle, -1
        def  _openMode, ax
        ddef _filename, es, dx                          ; arg passed internally as es:dx
        ddef _sftPointer                                ; sft reserved pointer
        defbytes _dirAccess, sizeDIRACCESS
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  does file exist ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, (FILE_ORDEVICE + FILECANNOT_BEDIRECTORY)
        mov si, dx                                      ; name from caller (es: si )
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile                                 ; if can't locate, then 
        ifc _SFTOpenFile_50                             ; error code already set

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  name is valid. populate sft.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        call FindAvailableSFTHandle                     ; find an available file handle
        ifc _SFTOpenFile_50                             ; if none available -->

        mov word ptr [ _handle ][ bp ], ax
        stordarg _sftPointer, es, bx                    ; sft reserved pointer
        call InitSFTEntry                               ; clear entry

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if file opened is a device
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getdarg dx, ax, _dirAccess.fileAcDevicePtr
        or dx, dx                                       ; _segment
        jz _SFTOpenFile_22                              ; if not a device -->

        or word ptr es:[ sftDevInfo ][ bx ], sftIsDevice
        mov word ptr es:[ sftDCB. _segment ][ bx ], dx
        mov word ptr es:[ sftDCB. _pointer ][ bx ], ax  ; device address

        push ds
        setDS ss
        mov si, word ptr [ _dirAccess.fileAcNameOffset ][ bp ]
        les di, dword ptr [ _sftPointer ][ bp ]         ; get sft pointer
        lea di, offset sftFileName [ di ]
        call convFilenametoFCBString                    ; convert to a match template
        pop ds

        jmp short _SFTOpenFile_42

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if open is to file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTOpenFile_22:
        push word ptr [ _dirAccess.fileAcBufferPtr. _segment ][ bp ]
        mov ax, word ptr [ _dirAccess.fileAcBufferPtr. _pointer ][ bp ]
        add ax, word ptr [ _dirAccess.fileAcDirOffset ][ bp ]
        push ax

        push word ptr [ _sftPointer. _segment ][ bp ]
        push word ptr [ _sftPointer. _pointer ][ bp ]
        call initSFTfromDirEntry

        mov al, byte ptr [ _openMode ][ bp ]
        mov byte ptr es:[ sftMode ][ di ], al           ; set mode.

        test al, OPEN_ACCESS_NOINHERIT
        jz _SFTOpenFile_42                              ; if inherit not used -->
        or word ptr es:[ sftDevInfo ][ di ], sftNoInherit

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTOpenFile_42:
        mov ax, word ptr [ _handle ][ bp ]
        clc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if error, release locked sft
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTOpenFile_50:
        getdarg es, di, _sftPointer                     ; sft reserved pointer
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Create SFT File                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:dx  unexpanded filename                                  ;
        ;   cx     attributes                                           ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:di  sft pointer                                          ;
        ;   zr     means end of file or wrong address                   ;
        ;...............................................................;

_SFTCreateFile:

        Entry
        def  _attributes, cx                            ; attributes
        defbytes _dirAccess, sizeDIRACCESS

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if path is valid
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov ax, (FILE_NODEVICENAME + FILEMAY_EXIST + FILECANNOT_BEDIRECTORY)
        mov si, dx                                      ; name from caller
        lea di, offset _dirAccess [ bp ]                ; work dir access block
        call LocateFile
        jc _SFTCreateFile_42                            ; if path invalid -->

        lea di, offset _dirAccess [ bp ]                ; work dir access block
        mov cx, word ptr [ _attributes ][ bp ]          ; get attributes
        call createSFTEntry                             ; create/init SFT entry

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
_SFTCreateFile_42:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read From SFT                                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx     bytes to read                                        ;
        ;   es:di  sft pointer                                          ;
        ;   stack  buffer address                                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cx     bytes actually read                                  ;
        ;   zr     means end of file or wrong address                   ;
        ;...............................................................;

_SFTReadFile:

        Entry 2
        darg _bufPtr

        def _readCount, cx
        ddef _sftpointer, es, di
        defbytes _diskAccess, sizeDISKACCESS

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is file redirected to a device ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        test word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        jz _SFTReadFile_14                              ; if file -->

        xor cx, cx                                      ; actually read
        test word ptr es:[ sftDevInfo ][ di ], sftEof   ; is it end of file ?
        jnz _SFTReadFile_12                             ; if end of file -->

        push word ptr es:[ sftDCB. _segment ][ di ]
        push word ptr es:[ sftDCB. _pointer ][ di ]
        mov cx, word ptr [ _readCount ][ bp ]
        getdarg es, di, _bufPtr
        call devCharReadLine                            ; read till cr or eof.
        jnz _SFTReadFile_10                             ; if not end of file -->

        getdarg es, di, _sftpointer
        or word ptr es:[ sftDevInfo ][ di ], sftEof     ; set end of file flag

_SFTReadFile_10:
        getdarg es, di, _bufPtr
        cmp cx, word ptr [ _readCount ][ bp ]           ; at maximum buffer ?
        jge _SFTReadFile_12                             ; yes -->

        add di, cx                                      ; add cr/ lf
        mov al, ControlM
        stosb                                           ; store cr

        inc cx                                          ; adjust return count
        cmp cx, word ptr [ _readCount ][ bp ]           ; at maximum buffer ?
        jge _SFTReadFile_12                             ; yes -->

        mov al, ControlJ
        stosb                                           ; store cr
        inc cx

_SFTReadFile_12:
        or cx, cx
        jmp _SFTReadFile_36

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is file read from stdin ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTReadFile_14:
        test word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        jz _SFTReadFile_20                              ; if file -->
        test word ptr es:[ sftDevInfo ][ di ], sftIsstdin
        jz _SFTReadFile_20                              ; if file -->

        push word ptr es:[ _bufPtr. _segment ][ di ]
        push word ptr es:[ _bufPtr. _pointer ][ di ]    ; buffer address
        call _SFTReadLine                               ; read line
        jmp _SFTReadFile_36

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  initialize disk access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTReadFile_20:
        or cx, cx                                       ; reading any ?
        jz _SFTReadFile_34                              ; if length is zero -->

        lea bx, _diskAccess [ bp ]                      ; build access control block
        call buildAccessRef                             ;  ...from sft

        mov ax, word ptr es:[ sftDevInfo    ][ di ]     ; get drive number
        call insureSFTSameVolume                        ; insure same volume
        jc _SFTReadFile_36                              ; read aborted -->

        xor cx, cx                                      ; bytes read (none yet )
        lea bx, _diskAccess [ bp ]                      ; build access control block
        getdarg es, di, _sftpointer                     ; restore sft pointer
        mov ax, word ptr es:[ sftFileSize. _low  ][ di ]
        mov dx, word ptr es:[ sftFileSize. _high ][ di ]

        sub ax, word ptr es:[ sftFilePosition. _low  ][ di ]
        sbb dx, word ptr es:[ sftFilePosition. _high ][ di ]
        jc _SFTReadFile_34                              ; if past end of file -->

        mov cx, word ptr _readCount [ bp ]              ; count: cx
        or dx, dx                                       ; more than 65k of file left ?
        jnz _SFTReadFile_26                             ; ok to continue reading -->

        cmp ax, cx                                      ; enough space to absorb read ?
        jnc _SFTReadFile_26                             ; ok to continue reading -->

        mov cx, ax                                      ; max read just to end of file
        or cx, cx                                       ; if zero, no need to read 
        jz _SFTReadFile_34

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if stdin, read line even if from a file
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTReadFile_26:
        mov ax, word ptr es:[ sftFileSize. _low  ][ di ]
        mov dx, word ptr es:[ sftFileSize. _high ][ di ]
        mov word ptr ss:[ diskAcFileSize. _low  ][ bx ], ax
        mov word ptr ss:[ diskAcFileSize. _high ][ bx ], dx

        mov ax, word ptr es:[ sftFilePosition. _low  ][ di ]
        mov dx, word ptr es:[ sftFilePosition. _high ][ di ]
        mov word ptr ss:[ diskAcPosition. _low  ][ bx ], ax
        mov word ptr ss:[ diskAcPosition. _high ][ bx ], dx

        getdarg es, di, _bufPtr                         ; buffer es: di
        call readLogicalBuffer                          ; Access buffer: ds: bx

        getdarg es, di, _sftPointer                     ; restore pointer to sft buffer
        add word ptr es:[ sftFilePosition. _low  ][ di ], cx
        adc word ptr es:[ sftFilePosition. _high ][ di ], 0000

        mov dx, word ptr ss:[ diskAcCurCluster ][ bx ]
        mov word ptr es:[ sftCurCluster ][ di ], dx
        call _computeRelativeCluster

_SFTReadFile_34:
        clc

_SFTReadFile_36:
        mov ax, cx                                      ; return count
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read Line From SFT                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   cx     max bytes to read                                    ;
        ;   es:di  sft pointer                                          ;
        ;   stack  buffer address                                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cx     bytes actually read                                  ;
        ;   zr     means end of file or wrong address                   ;
        ;...............................................................;

_SFTReadLine:

        Entry 2
        darg _bufPtr

        def  _readMax, cx
        def  _readActual, 0000
        ddef _sftpointer, es, di
        defbytes _diskAccess, sizeDISKACCESS

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is file redirected to a device 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        test word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        jz _SFTReadLine_14                              ; if file -->

        test word ptr es:[ sftDevInfo ][ di ], sftEof
        jz _SFTReadLine_36                              ; if at end of file -->

        push word ptr es:[ sftDCB. _segment ][ di ]
        push word ptr es:[ sftDCB. _pointer ][ di ]
        mov cx, word ptr [ _readMax ][ bp ]
        getdarg es, di, _bufPtr
        call devCharReadLine                            ; read till cr or eof.
        jnz _SFTReadLine_10                             ; if not end of file -->

        getdarg es, di, _sftpointer
        or word ptr es:[ sftDevInfo ][ di ], sftEof     ; set end of file flag

_SFTReadLine_10:
        getdarg es, di, _bufPtr
        cmp cx, word ptr [ _readMax ][ bp ]             ; at maximum buffer ?
        jge _SFTReadLine_12                             ; yes -->

        add di, cx                                      ; add cr/ lf
        mov al, ControlM
        stosb                                           ; store cr
        inc cx                                          ; adjust return count

_SFTReadLine_12:
        jmp _SFTReadLine_36

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  initialize disk access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTReadLine_14:
        or cx, cx                                       ; reading any ?
        jz _SFTReadLine_34                              ; if length is zero -->

        lea bx, _diskAccess [ bp ]                      ; build access control block
        call buildAccessRef                             ;  ...from sft

        mov ax, word ptr es:[ sftFileSize. _low  ][ di ]
        mov dx, word ptr es:[ sftFileSize. _high ][ di ]
        mov word ptr ss:[ diskAcFileSize. _low  ][ bx ], ax
        mov word ptr ss:[ diskAcFileSize. _high ][ bx ], dx

        mov ax, word ptr es:[ sftFilePosition. _low  ][ di ]
        mov dx, word ptr es:[ sftFilePosition. _high ][ di ]
        mov word ptr ss:[ diskAcPosition. _low  ][ bx ], ax
        mov word ptr ss:[ diskAcPosition. _high ][ bx ], dx

        getdarg es, di, _bufPtr                         ; buffer es: di
        mov cx, word ptr [ _readMax ][ bp ]             ; max bytes to read
        call readLine                                   ; read line

        getdarg es, di, _sftpointer
        mov word ptr es:[ sftFilePosition. _low  ][ di ], ax
        mov word ptr es:[ sftFilePosition. _high ][ di ], dx
        
        mov dx, word ptr ss:[ diskAcCurCluster ][ bx ]
        mov word ptr es:[ sftCurCluster ][ di ], dx
        call _computeRelativeCluster

_SFTReadLine_34:
        clc

_SFTReadLine_36:
        mov ax, cx                                      ; return count
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Write To SFT                                                 ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  sft pointer                                          ;
        ;   cx     bytes to write                                       ;
        ;   stack  buffer address                                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cx     bytes actually written                               ;
        ;   cy     end of available disk space reached                  ;
        ;...............................................................;

_SFTWriteFile:

        Entry 2
        darg _bufPtr

        def  _actuallyWritten, 0000
        def  _writeCount, cx
        ddef _sftpointer, es, di
        defbytes _diskAccess, sizeDISKACCESS
        defbytes _expandedBuffer, 128

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is file redirected to stdout device 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        test word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        ifz _SFTWriteFile_36                            ; if file -->
        test word ptr es:[ sftDevInfo ][ di ], sftIsstdout
        ifz _SFTWriteFile_36                            ; no -->
        or cx, cx                                       ; none to write ?
        ifz _SFTWriteFile_36                            ; special case -->

        getdarg es, di, _bufPtr                         ; get buffer address
        lea si, offset [ _expandedBuffer ][ bp ]
        lea dx, offset [ _expandedBuffer + 128 - 7][ bp ]

_SFTWriteFile_06:
        mov al, byte ptr es:[ di ]                      ; get character 
        mov byte ptr ss:[ si ], al                      ; store character
        inc si                                          ; advance pointer

        cmp al, ControlI                                ; tab character ?
        jnz _SFTWriteFile_08                            ; no, go see what it is -->

        mov al, 8
        mov ah, byte ptr [ _RxDOS_TabPosition ]         ; current tab position
        and ah, 7                                       ; tab offset
        sub al, ah                                      ; bytes to offset
        add byte ptr [ _RxDOS_TabPosition ], al         ; advance position

        cbw                                             ; 16 bit offset
        dec si                                          ; account for byte advance
        mov word ptr ss:[ si     ], '  '                ; add to expanded buffer
        mov word ptr ss:[ si + 2 ], '  '                ; add to expanded buffer
        mov word ptr ss:[ si + 4 ], '  '                ; add to expanded buffer
        mov word ptr ss:[ si + 6 ], '  '                ; add to expanded buffer
        add si, ax                                      ; quick fill
        jmp short _SFTWriteFile_16

_SFTWriteFile_08:
        cmp al, ControlJ                                ; line feed ?
        jz _SFTWriteFile_16                             ; yes -->
        inc byte ptr [ _RxDOS_TabPosition ]             ; else advance tab position

_SFTWriteFile_10:
        cmp al, ControlM                                ; carriage return ?
        jnz _SFTWriteFile_16                            ; no -->
        mov word ptr [ _RxDOS_TabPosition ], 0000       ; set tab position

_SFTWriteFile_16:
        inc di                                          ; advance pointer
        cmp si, dx                                      ; is expand buffer within tab of end ?
        jle _SFTWriteFile_20                            ; ok to continue -->

        push es
        push di                                         ; save current pointer into buffer
        push cx                                         ; counter
        push dx                                         ; counter

        lea cx, offset [ _expandedBuffer ][ bp ]        ; buffer address
        xchg cx, si                                     ; 
        sub cx, si                                      ; actual count
        push ss
        push si                                         ; buffer address

        getdarg es, di, _sftpointer                     ; sft pointer
        call _SFTWriteWithoutProcessing                 ; write buffer
        lea si, offset [ _expandedBuffer ][ bp ]        ; restore pointer

        pop dx                                          ; restore counter
        pop cx                                          ; restore counter
        pop di
        pop es                                          ; restore pointer

_SFTWriteFile_20:
        loop _SFTWriteFile_06                           ; if nore buffer

        lea cx, offset [ _expandedBuffer ][ bp ]        ; buffer address
        xchg cx, si                                     ; 
        sub cx, si                                      ; actual count
        jz _SFTWriteFile_24                             ; if nothing to write -->

        push ss
        push si                                         ; buffer address
        getdarg es, di, _sftpointer                     ; sft pointer
        call _SFTWriteWithoutProcessing                 ; write buffer

_SFTWriteFile_24:
        mov ax, word ptr [ _writeCount ][ bp ]          ; return count (stdOut requires this )
        or ax, ax
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  output
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTWriteFile_36:
        push word ptr [ _bufPtr. _segment ][ bp ]
        push word ptr [ _bufPtr. _pointer ][ bp ]       ; buffer address
        call _SFTWriteWithoutProcessing                 ; write buffer

        mov ax, cx                                      ; return actual written count
        or ax, ax
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Write To SFT Without Character Processing                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  sft pointer                                          ;
        ;   cx     bytes to write                                       ;
        ;   stack  buffer address                                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   cx     bytes actually written                               ;
        ;   cy     end of available disk space reached                  ;
        ;...............................................................;

_SFTWriteWithoutProcessing:

        Entry 2
        darg _bufPtr

        def _actuallyWritten, 0000
        def _writeCount, cx
        ddef _sftpointer, es, di
        defbytes _diskAccess, sizeDISKACCESS

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is file redirected to stdout device 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        test word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        jz _SFTWriteWithout_16

        or cx, cx
        jz _SFTWriteWithout_06                          ; if nothing to write -->

        push word ptr es:[ sftDCB. _segment ][ di ]
        push word ptr es:[ sftDCB. _pointer ][ di ]
        mov cx, word ptr [ _writeCount ][ bp ]
        getdarg es, di, _bufPtr
        call devCharWrite
        mov word ptr [ _actuallyWritten ][ bp ], cx

_SFTWriteWithout_06:
        jmp _SFTWriteWithout_56

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  initialize disk access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTWriteWithout_16:
        mov ax, word ptr es:[ sftDevInfo ][ di ]        ; get drive number
        or word ptr es:[ sftDevInfo ][ di ], sftWritten ; set written flag
        call insureSFTSameVolume                        ; insure same volume
        ifc _SFTWriteWithout_Error                      ; read aborted -->

        getdarg es, di, _sftpointer                     ; restore sft pointer
        cmp word ptr es:[ sftBegCluster ][ di ], 0000   ; begin cluster allocated ?
        jnz _SFTWriteWithout_24                         ; yes -->
        cmp word ptr [ _writeCount ][ bp ], 0000        ; not attempting to actually write
        jz _SFTWriteWithout_24                          ; if no write -->

        xor dx, dx
        mov ax, word ptr es:[ sftDevInfo    ][ di ]
        and ax, sftDrivemask
        call AllocateCluster                            ; allocate cluster
        ifc _SFTWriteWithout_56                         ; if can't allocate space -->

        getdarg es, di, _sftpointer
        mov word ptr es:[ sftBegCluster ][ di ], dx     ; save beginning cluster

_SFTWriteWithout_24:
        lea bx, _diskAccess [ bp ]                      ; build access control block
        call buildAccessRef                             ;  ...from sft
        mov word ptr [ _diskAccess. diskAcOptions ][ bp ], DISKAC_OPTIMIZEDWRITE

        xor cx, cx
        mov ax, word ptr es:[ sftFilePosition. _low  ][ di ]
        mov dx, word ptr es:[ sftFilePosition. _high ][ di ]
        sub ax, word ptr es:[ sftFileSize. _low  ][ di ]
        sbb dx, word ptr es:[ sftFileSize. _high ][ di ]
        jc _SFTWriteWithout_32                          ; if not past end of file -->
        or ax, dx                                       ; positive offset ?
        jz _SFTWriteWithout_32                          ; if not past end of file -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  file pointer past end of file.  fill to current pointer.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        push word ptr es:[ sftFileSize. _low  ][ di ]
        push word ptr es:[ sftFileSize. _high ][ di ]
        call fillLogicalBuffer                          ; Access buffer: ss: bx
        jc _SFTWriteWithout_56                          ; no more room on disk -->

        getdarg es, di, _sftPointer                     ; restore pointer to sft buffer
        add word ptr es:[ sftFileSize. _low  ][ di ], ax
        adc word ptr es:[ sftFileSize. _high ][ di ], dx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  write cx bytes to file from buffer passed
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTWriteWithout_32:
        getarg cx, _writeCount
        mov ax, word ptr es:[ sftFilePosition. _low  ][ di ]
        mov dx, word ptr es:[ sftFilePosition. _high ][ di ]
        getdarg es, di, _bufPtr                         ; buffer es: di
        call writeLogicalBuffer                         ; Access buffer: ss: bx

        getdarg es, di, _sftPointer                     ; restore pointer to sft buffer
        add word ptr es:[ sftFilePosition. _low  ][ di ], cx
        adc word ptr es:[ sftFilePosition. _high ][ di ], 0000
        add word ptr [ _actuallyWritten ][ bp ], cx

        mov dx, word ptr ss:[ diskAcCurCluster ][ bx ]
        mov word ptr es:[ sftCurCluster ][ di ], dx
        call _computeRelativeCluster

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  truncate size ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp word ptr [ _writeCount ][ bp ], 0000        ; was it truncate special case ?
        jnz _SFTWriteWithout_44                         ; no, go see if end of file moved -->

        mov ax, word ptr [ _diskAccess. diskAcDrive      ][ bp ]
        mov dx, word ptr [ _diskAccess. diskAcCurCluster ][ bp ]

        mov cx, word ptr es:[ sftFilePosition. _low  ][ di ]
        or cx, word ptr es:[ sftFilePosition. _high ][ di ] ; at beg of file ?
        jz _SFTWriteWithout_38                              ; yes -->

        call getNextCluster                             ; get next cluster, if any
        jz _SFTWriteWithout_46                          ; if already at end -->

_SFTWriteWithout_38:
        call ReleaseClusterChain                        ; truncate file to here
        jmp short _SFTWriteWithout_46

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  update file size
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTWriteWithout_44:
        mov ax, word ptr es:[ sftFilePosition. _low  ][ di ]
        mov dx, word ptr es:[ sftFilePosition. _high ][ di ]
        sub ax, word ptr es:[ sftFileSize. _low  ][ di ]
        sbb dx, word ptr es:[ sftFileSize. _high ][ di ]
        jc _SFTWriteWithout_56                             ; if not past end of file -->

_SFTWriteWithout_46:
        mov ax, word ptr es:[ sftFilePosition. _low  ][ di ]
        mov dx, word ptr es:[ sftFilePosition. _high ][ di ]
        mov word ptr es:[ sftFileSize. _low  ][ di ], ax
        mov word ptr es:[ sftFileSize. _high ][ di ], dx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTWriteWithout_56:
        mov cx, word ptr [ _actuallyWritten ][ bp ]
        mov ax, cx                                      ; return count
        or ax, ax
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  error return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTWriteWithout_Error:
        stc
        mov ax, offset pexterrInvalidAccess
        mov cx, word ptr [ _actuallyWritten ][ bp ]
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Close SFT File                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  SFT entry to close                                   ;
        ;                                                               ;
        ;...............................................................;

_SFTCloseFile:
        call _SFTCommitFile
        call releaseSFT
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Close All Files Owned By Current PSP                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;...............................................................;

_SFTCloseAllFiles:

        cmp word ptr ss:[ _RxDOS_CurrentPSP ], 0000
        jz _SFTCloseAllFiles_36                         ; if no app -->

        mov es, word ptr ss:[ _RxDOS_CurrentPSP ]       ; get PSP
        mov cx, word ptr es:[ pspFileHandleCount ]
        les si, dword ptr es:[ pspFileHandlePtr  ]

_SFTCloseAllFiles_04:
        cmp byte ptr es:[ si ], -1                      ; empty slot ?
        jz _SFTCloseAllFiles_14                         ; yes, skip entry -->

        SaveRegisters es, si, cx
        mov ax, word ptr es:[ pspFileHandleCount ]
        sub ax, cx                                      ; actual handle value 
        call MapAppToSFTHandle                          ; map to internal handle info
        call FindSFTbyHandle                            ; get corresponding SFT (es: di )
        jc _SFTCloseAllFiles_12                         ; if can't resolve handle, skip close
        call _SFTCloseFile                              ; do a normal close 

_SFTCloseAllFiles_12:
        RestoreRegisters cx, si, es
        mov byte ptr es:[ si ], -1                      ; make slot empty
        
_SFTCloseAllFiles_14:
        inc si                                          ; next
        loop _SFTCloseAllFiles_04                       ; continue -->

_SFTCloseAllFiles_36:
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Commit SFT File                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:di  SFT entry to close                                   ;
        ;                                                               ;
        ;...............................................................;

_SFTCommitFile:

        Entry
        ddef _sftPointer, es, di                        ; save sft buffer pointer

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  was device ever written to ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        test word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        ifnz _SFTCommitFile_36                          ; if device, no need to update -->
        test word ptr es:[ sftDevInfo ][ di ], sftWritten
        ifz _SFTCommitFile_36                           ; if not changed, don't update-->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  update dir entries from sft        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        mov ax, word ptr es:[ sftDevInfo    ][ di ]     ; get drive number
        call insureSFTSameVolume                        ; checked for changed drive 
        ifc _SFTCommitFile_36                           ; if not changed, don't update-->

        test word ptr es:[ sftDevInfo ][ di ], sftDateset ; date already set ?
        jnz _SFTCommitFile_16                           ; if date already set -->

        call getSysDateinDirFormat
        mov word ptr es:[ sftTime ][ di ], ax           ; time last updated
        mov word ptr es:[ sftDate ][ di ], dx           ; date last updated

_SFTCommitFile_16:
        push word ptr es:[ sftTime ][ di ]              ; time last updated
        push word ptr es:[ sftDate ][ di ]              ; date last updated

        or word ptr es:[ sftFileAttrib ][ di ], ATTR_ARCHIVE
        push word ptr es:[ sftFileAttrib ][ di ]
        push word ptr es:[ sftBegCluster ][ di ]
        push word ptr es:[ sftFileSize. _low  ][ di ]
        push word ptr es:[ sftFileSize. _high ][ di ]

    ; get dir entry

        mov al, byte ptr es:[ sftDirIndex ][ di ]       ; get offset into directory
        and ax, maskDIRINDEX                            ; all we need is bottom bits
        mov cl, sizeDIRENTRY
        mul cl                                          ; compute offset into dir entry
        push ax                                         ; save offset into directory sector

        mov dx, word ptr es:[ sftDirSector. _low  ][ di ]
        mov cx, word ptr es:[ sftDirSector. _high ][ di ]
        mov ax, word ptr es:[ sftDevInfo    ][ di ]
        and ax, sftDrivemask
        call readBuffer                                 ; returns es:di pointer to buffer
        or byte ptr es:[ ccbStatus ][ di ], ( ccb_isDIR )

    ; update dir entry

        pop bx                                          ; dir offset
        pop word ptr es:[ ccbData. deFileSize. _high ][ di + bx ]
        pop word ptr es:[ ccbData. deFileSize. _low  ][ di + bx ]
        pop word ptr es:[ ccbData. deStartCluster   ][ di + bx ]

        pop ax
        mov byte ptr es:[ ccbData. deAttributes ][ di + bx ], al

        pop word ptr es:[ ccbData. deDate ][ di + bx ]  ; date created
        pop word ptr es:[ ccbData. deTime ][ di + bx ]  ; time created

        call CCBChanged
        call updateAllChangedCCBBuffers                 ; force update of buffer

        getdarg es, di, _sftPointer                     ; save sft buffer pointer
        cmp word ptr es:[ sftDevInfo ][ di ], sftIsDevice
        jnz _SFTCommitFile_36                           ; if device -->
        and word ptr es:[ sftDevInfo ][ di ], not (sftWritten + sftDateset)

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  release sft
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

_SFTCommitFile_36:
        clc
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Test If Handle Table is Relocatable                          ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  bx number of file handles to allocate to an application      ;
        ;...............................................................;

TestIfMoveHandleTable:

        push es
        push ds

        mov dx, bx                                      ; updated # handles
        cmp dx, sizePSPHandleTable                      ; more than 20 ?
        jg testHandleTbl_20                             ; can't move if more than 20 ->
        cmp word ptr ss:[ _RxDOS_CurrentPSP ], 0000
        jz testHandleTbl_20                             ; can't move without a PSP -->

        mov es, word ptr ss:[ _RxDOS_CurrentPSP ]       ; get PSP
        mov cx, word ptr es:[ pspFileHandleCount ]
        sub cx, sizePSPHandleTable                      ; more than 20 ?        
        jle testHandleTbl_20                            ; no point to move if > 20 -->

        mov ax, es
        cmp ax, word ptr es:[ pspFileHandlePtr. _segment ]; is table outside of PSP ?
        jz testHandleTbl_20                             ; if not outside of PSP -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  scan table
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        les bx, dword ptr es:[ pspFileHandlePtr ]       ; get JHT address
        add bx, 20                                      ; start searching at handle 20

testHandleTbl_08:
        cmp byte ptr es:[ bx ], -1                      ; is entry free ?
        jnz testHandleTbl_20                            ; no, can't relocate -->

        inc bx                                          ; incr pointer
        loop testHandleTbl_08                           ; if more -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  ok, relocate table
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov es, word ptr ss:[ _RxDOS_CurrentPSP ]       ; get PSP
        lds si, dword ptr es:[ pspFileHandlePtr ]       ; get pointer to old table
        mov di, offset pspHandleTable

        mov cx, dx                                      ; updated count (20 or less)
        mov word ptr es:[ pspFileHandleCount ], cx      ; destination count
        mov word ptr es:[ pspFileHandlePtr. _segment ], es
        mov word ptr es:[ pspFileHandlePtr. _pointer ], di

        mov ax, -1
        mov cx, sizePSPHandleTable/2
        rep stosw                                       ; init table to -1

        mov cx, dx
        mov di, offset pspHandleTable
        rep movsb                                       ; copy table

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  free extra mem block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor bx, bx
        mov ax, ds                                      ; points to old memory block
        dec ax
        mov ds, ax                                      ; is this a valid memory block ?
        cmp byte ptr [ _memSignature ][ bx ], _RxDOS_MEMSIGNATURE
        jz testHandleTbl_14
        cmp byte ptr [ _memSignature ][ bx ], _RxDOS_ENDSIGNATURE
        jnz testHandleTbl_20

testHandleTbl_14:
        mov word ptr [ _memParent ][ bx ], 0000         ; free memory block

testHandleTbl_20:
        pop ds
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Insure Sft Volume Id same as Current Installed.              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   es:di  segment/ pointer of SFT                              ;
        ;                                                               ;
        ;...............................................................;

insureSFTSameVolume:

        Entry  
        def  _drive    
        ddef _dpbpointer
        ddef _sftpointer, es, di

        saveAllRegisters
        and ax, sftDrivemask
        storarg _drive, ax

insureSFTSameVolume_08:
        getarg ax, _drive
        call getDPB                                     ; get current DPB
        stordarg _dpbpointer, es, bx

        mov cx, word ptr es:[ _dpbVolumeId. _High ][ bx ]
        mov dx, word ptr es:[ _dpbVolumeId. _Low  ][ bx ]

        getdarg es, di, _sftpointer
        cmp cx, word ptr es:[ sftVolumeId. _High ][ di ]
        jnz insureSFTSameVolume_12                      ; not same -->
        cmp dx, word ptr es:[ sftVolumeId. _Low  ][ di ]
        jz insureSFTSameVolume_36                      ; same, exit no carry -->

insureSFTSameVolume_12:
        getarg ax, _drive
        mov cx, word ptr es:[ sftVolumeId. _High ][ di ]
        mov dx, word ptr es:[ sftVolumeId. _Low  ][ di ]
        call incorrectDiskMedia

        Goto CRITERROR_TERMINATE    , insureSFTSameVolume_16
        Goto CRITERROR_FAIL         , insureSFTSameVolume_16
        Goto CRITERROR_RETRY        , insureSFTSameVolume_08

        clc
        jmp short insureSFTSameVolume_36                ; if CRITERROR_IGNORE or if correct ->

insureSFTSameVolume_16:
        mov ax, offset pexterrInvalidAccess
        storarg _drive, ax                              ; return value
        stc

insureSFTSameVolume_36:
        restoreAllRegisters
        getarg ax, _drive
        mov cx, 0
        Return

RxDOS   ENDS
        END

