        TITLE   'fil - DOS Redirected File System Support'
        PAGE 59, 132
        .LALL

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  DOS Redirected File System Support                           ;
        ;...............................................................;

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  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

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  DOS Redirected File System Support                           ;
        ;...............................................................;

        public initdiskAccess
        public computeLogSectorNumber
        public readLogicalBuffer
        public writeLogicalBuffer
        public fillLogicalBuffer

        public readLine

        public ExpandFileName
        public LocateFile
        public LocateFileByAttribute
        public LocateFreeDirSlot
        public blankinitDirName
        public scanDirectory
        public compareDirEntries

        public GetActualDrive
        public getCurrDirCluster
        public getDevice
        public getDrive
        public getWhereInDir

        extrn scanInvalidFilenameChars          : near
        extrn ifPathSeparator                   : near
        extrn upperCase                         : near

        extrn convFCBNametoASCIZ                : near
        extrn skipToNextName                    : near
        extrn skipToLast                        : near
        extrn getAddrDPB                        : near
        extrn getDPB                            : near

        extrn _div32                            : near
        extrn convFilenametoFCBString           : near
        extrn locateCCBPHeader                  : near
        extrn CCBChanged                        : near
        extrn selBuffer                         : near
        extrn updateAllChangedCCBBuffers        : near

        extrn AppendCluster                     : near
        extrn AllocateInitCluster               : near
        extrn _FATReadRandom                    : near
        extrn updateClusterValue                : near
        extrn CompareString                     : near
        extrn StringLength                      : near

        extrn checkforDeviceName                : near
        extrn _RxDOS_pCDS                       : dword
        extrn _RxDOS_bLastDrive                 : byte
        extrn _RxDOS_CurrentDrive               : byte

        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

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Init System Access Block                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   dx     cluster of dir to search                             ;
        ;   ss:bx  address of disk access structure                     ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   all registers preserved                                     ;
        ;...............................................................;

initdiskAccess:

        push es
        push cx
        push di
        push ax

        setES ss                                        ; set segment
        mov di, bx                                      ; pointer to di
        clearMemory sizeDISKACCESS

        pop ax
        xor ah, ah
        mov word ptr es:[ diskAcDrive ][ bx ], ax
        mov word ptr es:[ diskAcBegCluster ][ bx ], dx

        pop di
        pop cx
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Compute Logical Sector Number                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   dx     cluster number                                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     drive                                                ;
        ;   cx:dx  logical sector number                                ;
        ;...............................................................;

computeLogSectorNumber:

        push es
        push bx

        push ax                                         ; drive
        push dx                                         ; cluster 

        call getAddrDPB                                 ; get first sector address from DPB
        mov dx, word ptr es:[ _dpbFirstDirSector ][ bx ]
        xor cx, cx                                      ; if cluster is zero
        pop ax                                          ; get cluster number
        or ax, ax                                       ; if reading cluster 0000
        jz computeLogSectorNumber_12                    ; LSN is zero -->

        dec ax
        dec ax
        mov cx, 1
        add cl, byte ptr es:[ _dpbClusterSizeMask ][ bx ]
        mul cx                                          ; compute sector
        add ax, word ptr es:[ _dpbFirstDataSector ][ bx ]
        adc dx, 0000
        mov cx, dx                                      ; high part
        mov dx, ax                                      ; low part

computeLogSectorNumber_12:
        pop ax                                          ; restore drive
        pop bx
        pop es
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Expand File Name                                             ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     options: FILECANNOT_BEDIRECTORY                      ;
        ;   arg    ptr to filename passed                               ;
        ;   arg    ptr to expanded filename                             ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:di  ptr to expanded filename                             ;
        ;   dx     current cluster, if search to begin here             ;
        ;   ax     drive, or error                                      ;
        ;   cy     if error detected                                    ;
        ;...............................................................;

ExpandFileName:

        Entry 4
        darg _UnexpandedFileName
        darg _ExpandBuffer
        def  _options, ax

        xor ax, ax
        def _drive, ax                                  ; initialize to 0's
        def _currCluster, ax
        def _cds_SubstOffset, ax
        def _NetworkPathName, ax                        ; False
        def _startPointer

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  skip through any leading spaces
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        push ds
        currSegment ds                                  ; point to default segment
        les si, dword ptr [ _UnexpandedFileName ][ bp ]
        call getDrive
        storarg _drive, ax                              ; save argument
        ifc expandFileName_Error                        ; invalid drive -->

        call scanInvalidFilenameChars                   ; make sure filename is ok
        ifc expandFileName_Error                        ; if error -->

        mov word ptr [ _UnexpandedFileName. _pointer ][ bp ], si

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get selected drive current directory
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getarg ax, _drive
        mov cl, sizeCDS
        mul cl                                          ; ax contains offset to current drive

        lds si, dword ptr ss:[ _RxDOS_pCDS ]            ; actual address in CDS
        add si, ax                                      ; from
        add si, _cdsActualDirectory                     ; proper offset

        xor cx, cx
        mov cl, byte ptr [ _cdsNonSubstOffset ][ si ]
        mov word ptr [ _cds_SubstOffset ][ bp ], cx

        mov dx, word ptr [ _cdsStartClusterDir ][ si ]
        mov word ptr [ _currCluster ][ bp ], dx         ; current starting cluster

        push ds
        push si
        call StringLength                               ; length of source string

        getdarg es, di, _ExpandBuffer                   ; address of expanded buffer
        rep movsb                                       ; copy buffer

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  does path end with a \ or / ? 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp byte ptr es:[ di - 1 ], '/'                 ; ends with trailing \ ?
        jz expandFileName_18                            ; yes, else add -->
        cmp byte ptr es:[ di - 1 ], '\'                 ; ends with trailing \ ?
        jz expandFileName_18                            ; yes, else add -->
        inc di                                          ; add trailing \

expandFileName_18:
        mov byte ptr es:[ di - 1 ], '\'                 ; insure ends with \ char

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  does path start with root reference ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        lds si, dword ptr [ _UnexpandedFileName ][ bp ]
        cmp byte ptr [ si ], '\'
        jz expandFileName_20
        cmp byte ptr [ si ], '/'
        jz expandFileName_20

        cmp byte ptr [ si ], ' '+1
        ifc expandFileName_Error                        ; null strings not parsed -->
        jmp short expandFileName_30                     ; continue -->

expandFileName_20:
        mov di, word ptr [ _ExpandBuffer. _pointer ][ bp ]
        add di, word ptr [ _cds_SubstOffset ][ bp ]
        xor ax, ax
        mov byte ptr es:[ di ], al                      ; append terminator
        mov word ptr [ _currCluster ][ bp ], ax         ; path name info supplied
        inc si                                          ; skip starting '\'

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  scan for only valid characters
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

expandFileName_30:
        storarg _startPointer, si

expandFileName_32:
        lodsb

        call upperCase
        stosb                                           ; save at es:di
        cmp al, ' '+1                                   ; end of string ?
        jc expandFileName_36                            ; yes -->

        call ifPathSeparator                            ; path separator ?
        jnz expandFileName_32                           ; not yet -->
        mov byte ptr es:[ di - 1 ], al                  ; make sure separator is \
        mov word ptr [ _currCluster ][ bp ], 0000       ; path name info supplied

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  validate path separator
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

expandFileName_36:
        mov byte ptr es:[ di ], 0                       ; null terminate

        mov cx, si
        sub cx, word ptr [ _startPointer ][ bp ]        ; length of string
        dec cx                                          ; excluding last \ ...
        jg expandFileName_38                            ; if more than one character -->
        cmp al, '\'                                     ; terminates with a \ ?
        ifz expandFileName_Error                        ; \\ combination not permitted ->
        cmp al, '/'                                     ; terminates with a / ?
        ifz expandFileName_Error                        ; // combination not permitted ->

expandFileName_38:
        cmp cx, 2                                       ; 
        jg expandFileName_56                            ; if not . or .., go get next -->

        test word ptr [ _options ][ bp ], FILECANNOT_BEDIRECTORY
        jz expandFileName_44
        cmp byte ptr es:[ di - 1 ], '\'                 ; will always be \ in expansion string
        jnz expandFileName_56                           ; no, don't test for . and .. entries -->

expandFileName_44:
        cmp word ptr es:[ di - 3 ], '..'                ; back subdirectory
        jz expandFileName_50                            ; go backup to previous -->
        cmp word ptr es:[ di - 3 ], '.\'                ; current subdirectory
        jz expandFileName_48                            ; all set -->
        cmp word ptr es:[ di - 3 ], './'                ; current subdirectory
        jnz expandFileName_56                           ; all set -->

expandFileName_48:
        sub di, cx                                      ; 
        dec di                                          ; fix current
        jmp short expandFileName_56                     ; go process next -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  backup to previous directory
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

expandFileName_50:
        mov word ptr [ _currCluster ][ bp ], 0000       ; no longer current cluster

        sub di, cx                                      ; 
        dec di                                          ; fix to current,

        mov cx, di
        sub cx, word ptr [ _ExpandBuffer. _pointer ][ bp ]
        sub cx, word ptr [ _cds_SubstOffset ][ bp ]
        jle expandFileName_56

expandFileName_52:
        dec di
        cmp byte ptr es:[ di - 1 ], '\'
        jz expandFileName_56
        cmp byte ptr es:[ di - 1 ], '/'
        jz expandFileName_56
        loop expandFileName_52

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  more to go ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

expandFileName_56:
        mov cx, di
        sub cx, word ptr [ _ExpandBuffer. _pointer ][ bp ]
        cmp cx, word ptr [ _cds_SubstOffset ][ bp ]
        jge expandFileName_60

        mov di, word ptr [ _cds_SubstOffset ][ bp ]
        add di, word ptr [ _ExpandBuffer. _pointer ][ bp ]

expandFileName_60:
        cmp byte ptr [ si - 1 ], ' '+1                  ; was null ?
        ifnc expandFileName_30                          ; no, go get next -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  clean up string at end 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        xor ax, ax
        mov byte ptr es:[ di ], al                      ; add terminator

        mov di, word ptr [ _ExpandBuffer. _pointer ][ bp ]
        mov cx, 128
        repnz scasb                                     ; scan for null terminator

        dec di
        mov cx, di
        sub cx, word ptr [ _ExpandBuffer. _pointer ][ bp ]
        cmp cx, word ptr [ _cds_SubstOffset ][ bp ]
        jle expandFileName_66                           ; if minimal string -->

        cmp byte ptr es:[ di - 1 ], '\'                 ; string ends with \ ?
        jz expandFileName_64                            ; no, ignore fix up -->
        cmp byte ptr es:[ di - 1 ], '/'                 ; string ends with / ?
        jnz expandFileName_66                           ; no, ignore fix up -->

expandFileName_64:
        mov byte ptr es:[ di - 1 ], 0                   ; kill any terminating (needless \)

expandFileName_66:
        getarg ax, _drive
        or ax, ax                                       ; return drive.
        jmp short expandFileName_84

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

expandFileName_Error:
        stc
        mov ax, offset pexterrPathNotFound

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

expandFileName_84:
        les di, dword ptr [ _ExpandBuffer ][ bp ]       ; set return pointer.
        mov dx, word ptr [ _currCluster ][ bp ]         ; where to start search.
        pop ds
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Locate/Validate File                                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  This function takes a FCB pointer or ASCIZ path name and     ;
        ;  determines whether the name and path are valid, and whether  ;
        ;  the file exists.  It returns a pointer to the file entry     ;
        ;  in a directory buffer.                                       ;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:si  pointer to input filename or fcb.                    ;
        ;   ss:di  pointer to directory work area.                      ;
        ;   ax     options, as follows:                                 ;
        ;                                                               ;
        ;   FILEIS_FCB               name is an FCB                     ;
        ;   FILE_NODEVICENAME        no device name allowed             ;
        ;   FILEHAS_WILDCHARS        allowed in name                    ;
        ;   FILEHAS_NOFILENAME       no filename expected               ;
        ;   FILECANNOT_BEDEFINED     filename must not exist            ;
        ;   FILECANNOT_BEDIRECTORY   filename cannot be directory       ;
        ;   FILEMAY_EXIST            file may exist (cluster not -1 )   ;
        ;   FILE_ORDEVICE            file or device must exist          ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ss:di  pointer to directory work area.                      ;
        ;   dx     cluster address of located file                      ;
        ;   cx     cluster address of dir in which located file found   ;
        ;   ax     drive                                                ;
        ;   cy     means path/filename is not valid.                    ;
        ;                                                               ;
        ;  Assumes ss == ds                                             ;
        ;...............................................................;

LocateFile:

        Entry

        def  _drive
        def  _cluster, 0000
        def  _dirCluster, 0000
        def  _terminatingChar
        def  _begfilenamePointer
        ddef _endfilenamePointer

        def  _options, ax
        ddef _filename, es, si
        ddef _dirLocate, ss, di
        defbytes _tempFileName, sizeTempFILENAME
        defbytes _diskAccess, sizeDISKACCESS

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  clear/ init dir access block
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        push es
        push ds

        push es
        setES ss
        clearMemory sizeDIRACCESS
        pop es                                          ; restore source segment

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  expand name to a usable form
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        setDS ss
        mov di, word ptr [ _dirLocate. _pointer ][ bp ]
        lea di, offset fileAcExpandedName [ di ]        ; expand name to store
        test word ptr _options [ bp ], FILEIS_FCB
        jz locateFile_12                                ; if not fcb -->

        getarg ax, _options
        saveRegisters es, si, ss, di                    ; arguments
        call convFCBNametoASCIZ                         ; build asciz name

locateFile_12:
        saveRegisters es, si, ss, di                    ; arguments
        call ExpandFileName                             ; expanded filename
        mov word ptr [ _cluster ][ bp ], dx             ; where to start search
        ifc locateFile_PathNotFound                     ; if path invalid -->
        
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get disk parameters
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFile_16:
        les si, dword ptr [ _dirLocate ][ bp ]          ; point to expanded filename
        lea si, offset fileAcExpandedName [ si ]        ; expanded name store
        call getDrive                                   ; extract drive name (es:si)
        mov word ptr [ _drive ][ bp ], ax               ; get drive.
        stordarg _endfilenamePointer, es, si            ; save ptr to working dir
        ifc locateFile_PathNotFound                     ; if illegal drive -->

        call getDPB                                     ; make sure drive is initialized
     ;  call initDriveParameters                        ; get drive parameters
        ifc locateFile_PathNotFound                     ; if illegal drive -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if search only current directory, skip to name
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cmp word ptr [ _cluster ][ bp ], 0000           ; if NOT search in subdirectory  
        jz locateFile_20                                ; ok as name is -->

        les si, dword ptr [ _endfilenamePointer ][ bp ]
        call skipToLast                                 ; skip to starting name
        stordarg _endfilenamePointer, es, si

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get next name
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFile_20:
        setES ss
        lea di, offset _tempFileName [ bp ]             ; pointer to temp name
        lds si, dword ptr [ _endfilenamePointer ][ bp ]
        call skipToNextName                             ; skip to starting name
        storarg _begfilenamePointer, si                 ; save starting pointer
        call convFilenametoFCBString                    ; convert to a match template
        jnz locateFile_24                               ; if name is not blank -->

        test word ptr _options [ bp ], FILECANNOT_BEDIRECTORY
        ifnz locateFile_FileNotFound                    ; if cannot be a directory -->
        jmp locateFile_56                               ; else return what we have so far -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  scan for wild characters
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFile_24:
        stordarg _endfilenamePointer, ds, si
        mov word ptr [ _terminatingChar ][ bp ], ax

        setES ss
        lea di, offset _tempFileName [ bp ]             ; pointer to temp name
        mov cx, sizeTempFILENAME - 1                    ; count
        mov al, '?'
        repnz scasb                                     ; scan for wild characters
        jnz locateFile_26                               ; if no wild chars found -->

        cmp byte ptr [ _terminatingChar ][ bp ], '\'  
        ifz locateFile_PathNotFound                     ; inside path def, error -->
        cmp byte ptr [ _terminatingChar ][ bp ], '/'  
        ifz locateFile_PathNotFound                     ; inside path def, error -->
        test word ptr _options [ bp ], FILEHAS_WILDCHARS
        ifz locateFile_FileNotFound                     ; if wild chars NOT allowed -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if entry is a device name
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFile_26:
        currSegment ds, es                              ; set to seg address
        lea si, offset _tempFileName [ bp ]             ; search es: si
        call getDevice                                  ; determine if it's a device name
        ifc locateFile_32                               ; if not a device name -->

     ;  here if device name 

        cmp byte ptr [ _terminatingChar ][ bp ], '\'    ; was name part of path ?
        ifz locateFile_PathNotFound                     ; inside path def, error -->
        cmp byte ptr [ _terminatingChar ][ bp ], '/'    ; was name part of path ?
        ifz locateFile_PathNotFound                     ; inside path def, error -->
      ; test word ptr _options [ bp ], FILEHAS_NOFILENAME ; if no filename, then definetly 
      ; ifz locateFile_PathNotFound                     ; inside path def, error -->
        test word ptr _options [ bp ], FILE_NODEVICENAME; else if in filename, it is allowed ?
        ifnz locateFile_FileNotFound                    ; error if no device names allowed -->

        clc                                             ; if NO error
        mov si, word ptr [ _dirLocate. _pointer ][ bp ]
        mov word ptr ss:[ fileAcDevicePtr. _segment ][ si ], es
        mov word ptr ss:[ fileAcDevicePtr. _pointer ][ si ], bx
        mov ax, 8000h
        jmp locateFile_60

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  scan for path in directory
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFile_32:
        currSegment ds, es
        lea di, offset _tempFileName [ bp ]             ; search es: di

        mov ax, word ptr [ _drive   ][ bp ]             ; get drive
        mov dx, word ptr [ _cluster ][ bp ]             ; cluster to search next
        mov word ptr [ _dirCluster ][ bp ], dx          ; start cluster of directory

locateFile_36:
        call scanDirectory                              ; scan for name in directory
        jnc locateFile_48                               ; if an item was found -->

        cmp byte ptr [ _terminatingChar ][ bp ], '\'
        jz locateFile_PathNotFound                      ; inside path def, error -->
        cmp byte ptr [ _terminatingChar ][ bp ], '/'
        jz locateFile_PathNotFound                      ; inside path def, error -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  entry not found or matched.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        or word ptr [ _drive ][ bp ], 8000h             ; not found.

        test word ptr _options [ bp ], (FILECANNOT_BEDEFINED + FILEMAY_EXIST)
        jnz locateFile_56                               ; if not defined is ok -->
        jmp short locateFile_FileNotFound               ; not found -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is FOUND entry a filename ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFile_48:
        mov word ptr [ _cluster ][ bp ], dx             ; cluster to search next

        test bx, (ATTR_VOLUME + ATTR_DIRECTORY)         ; is entry a directory ?
        jnz locateFile_50                               ; if not a file -->

        cmp byte ptr [ _terminatingChar ][ bp ], '\'
        jz locateFile_PathNotFound                      ; if path expected, error -->
        cmp byte ptr [ _terminatingChar ][ bp ], '/'
        jz locateFile_PathNotFound                      ; if path expected, error -->

        test word ptr _options [ bp ], (FILECANNOT_BEDEFINED + FILEHAS_NOFILENAME)
        jnz locateFile_AccessDenied                     ; if access denied -->
        jmp short locateFile_Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  is FOUND entry a directory ?
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFile_50:
        test bx, ATTR_DIRECTORY                         ; is item a directory ?
        jz locateFile_FileNotFound                      ; if not, its an error -->
        cmp byte ptr [ _terminatingChar ][ bp ], '\'
        ifz locateFile_20                               ; if path expected, get next -->
        cmp byte ptr [ _terminatingChar ][ bp ], '/'
        ifz locateFile_20                               ; if path expected, get next -->

        test word ptr _options [ bp ], FILECANNOT_BEDIRECTORY
        jnz locateFile_FileNotFound                     ; if cannot be a directory -->
        test word ptr _options [ bp ], FILECANNOT_BEDEFINED
        jz locateFile_Return                            ; if everything is ok -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  in case of error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFile_FileNotFound:
        SetError pexterrFileNotFound, locateFile_60

locateFile_PathNotFound:
        SetError pexterrPathNotFound, locateFile_60

locateFile_AccessDenied:
        SetError pexterrAccessDenied, locateFile_60

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFile_Return:
        call locateCCBPHeader                           ; si dir pointer/ di ccb

        mov bx, word ptr [ _dirLocate. _pointer ][ bp ]
        mov word ptr [ fileAcBufferPtr. _segment ][ bx ], es
        mov word ptr [ fileAcBufferPtr. _pointer ][ bx ], di
        mov word ptr [ fileAcDirOffset ][ bx ], cx      ; offset in dir sector

        mov ax, word ptr es:[ ccbLBN. _low  ][ di ]
        mov dx, word ptr es:[ ccbLBN. _high ][ di ]
        mov word ptr [ fileAcDirSector. _low  ][ bx ], ax; which dir sector
        mov word ptr [ fileAcDirSector. _high ][ bx ], dx

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFile_56:
        mov bx, word ptr [ _dirLocate. _pointer ][ bp ] ; restore bx
        mov dx, word ptr [ _cluster ][ bp ]             ; cluster
        mov ax, word ptr [ _drive  ][ bp ]              ; drive
        mov word ptr [ fileAcCluster ][ bx ], dx
        mov word ptr [ fileAcDrive ][ bx ], ax

        mov cx, word ptr [ _dirCluster ][ bp ]          ; start cluster of directory
        mov word ptr [ fileAcDirCluster ][ bx ], cx
        clc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFile_60:
        push ax
        currSegment ds
        mov bx, word ptr [ _dirLocate. _pointer ][ bp ]
        mov ax, word ptr [ _begfilenamePointer ][ bp ]
        mov word ptr [ fileAcNameOffset ][ bx ], ax
        
        mov ax, word ptr [ _drive  ][ bp ]              ; drive
        mov word ptr [ fileAcDrive ][ bx ], ax

        pop ax
        pop ds
        pop es
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Locate File by Attribute                                     ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  (important: starts search at offset in 'findDirEntry')       ;
        ;                                                               ;
        ;  es:di  pointer to find access block (DTA area)               ;
        ;...............................................................;

LocateFileByAttribute:

        Entry
        def  _attributes
        ddef _findAccess, es, di
        defbytes _diskAccess, sizeDISKACCESS
        defbytes _tempname, sizeTempExpandedFCBNAME

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init access
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        saveSegments
        getdarg es, di, _findAccess                     ; point to find access

        xor ah, ah
        mov al, byte ptr es:[ findSrchDrive     ][ di ]
        mov dx, word ptr es:[ findDirBegCluster ][ di ]

        mov cl, byte ptr es:[ findSrchAttributes][ di ]
        and cl, ATTR_MASK
        mov byte ptr [ _attributes ][ bp ], cl          ; save for fast search

        setES ss
        lea bx, offset _diskAccess [ bp ]               ; pointer to access block
        call initdiskAccess                             ; [ax] is drive, [dx] is cluster
        call getAddrDPB                                 ; see if drive initialized

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  lookup entry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getdarg es, di, _findAccess                     ; point to find access
        mov cx, sizeDIRENTRY
        mov ax, word ptr es:[ findDirEntry ][ di ]
        cmp ax, -1
        jnz locByAttrib_14

        xor ax, ax
        mov word ptr es:[ findDirEntry ][ di ], ax

locByAttrib_14:
        mul cx

        sub ax, sizeDIRENTRY                            ; (not same as dec ax before mult)
        sbb dx, 0000                                    ; just adjust for next loop
        mov word ptr [ _diskAccess. diskAcPosition. _low  ][ bp ], ax
        mov word ptr [ _diskAccess. diskAcPosition. _high ][ bp ], dx
        mov word ptr [ _diskAccess. diskAcOptions         ][ bp ], (ccb_isDIR)

locByAttrib_18:
        setDS ss                                        ; insure ds == ss

        lea bx, offset _diskAccess [ bp ]               ; pointer to access block
        add word ptr [ diskAcPosition. _low  ][ bx ], sizeDIRENTRY
        adc word ptr [ diskAcPosition. _high ][ bx ], 0000
        call _FATReadRandom                             ; read into buffer
        ifz locByAttrib_54                              ; if no more data -->

        lea di, offset deName[ bx ]                     ; get pointer to name
        cmp byte ptr es:[ di ], DIRENTRY_NEVERUSED
        ifz locByAttrib_54                              ; if no more data -->

        cmp byte ptr es:[ di ], DIRENTRY_DELETED
        jz locByAttrib_18                               ; don't bother -->

        lds si, dword ptr [ _findAccess ][ bp ]         ; pointer to search file name
        lea si, offset findSrchName[ si ]               ;
        call compareDirEntries
        jnz locByAttrib_18                              ; if item not found -->

        mov cl, byte ptr [ _attributes ][ bp ]          ; template attributes
        mov ch, byte ptr es:[ deAttributes ][ bx ]
        and ch, ATTR_MASK                               ; AND (NOT ATTR_READONLY)
        test ch, cl                                     ; test for special cases
        jnz locByAttrib_42                              ; if bit match -->

        or cl, cl                                       ; which files ?
        jz locByAttrib_22                               ; just normal files -->

        test cl, (ATTR_HIDDEN + ATTR_SYSTEM + ATTR_DIRECTORY)
        jz locByAttrib_18                               ; skip normal files -->

locByAttrib_22:
        and ch, (not ATTR_READONLY)                     ; is file a normal file ?
        jnz locByAttrib_18                              ; normal file filter -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  item found
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locByAttrib_42:
        push bx
        push word ptr es:[ deDate            ][ bx ]
        push word ptr es:[ deTime            ][ bx ]
        push word ptr es:[ deFileSize. _low  ][ bx ]
        push word ptr es:[ deFileSize. _high ][ bx ]
        push word ptr es:[ deAttributes      ][ bx ] 

        lea si, offset deName-1 [ bx ]                  ; drive here doesn't matter
        lea di, offset _tempname [ bp ]                 ; expand name

        saveRegisters es, si, ss, di                    ; arguments
        call convFCBNametoASCIZ                         ; expand name

        pop ax
        getdarg ds, di, _findAccess                     ; point to find access
        mov byte ptr [ findFileAttribute   ][ di ], al
        pop word ptr [ findFileSize. _high ][ di ]
        pop word ptr [ findFileSize. _low  ][ di ]
        pop word ptr [ findFileTime        ][ di ]
        pop word ptr [ findFileDate        ][ di ]
        pop word ptr [ findCCBPointer      ][ di ]

        mov ax, word ptr [ _diskAccess. diskAcPosition. _low  ][ bp ]
        mov dx, word ptr [ _diskAccess. diskAcPosition. _high ][ bp ]
        mov cx, sizeDIRENTRY
        call _div32
        mov word ptr [ findDirEntry ][ di ], ax

        mov ax, word ptr [ _diskAccess. diskAcBegCluster ][ bp ]
        mov dx, word ptr [ _diskAccess. diskAcCurCluster ][ bp ]
        mov word ptr [ findDirBegCluster  ][ di ], ax
        mov word ptr [ findDirCurrCluster ][ di ], dx
               
        setDS ss
        lea si, [ _tempname + 2 ][ bp ]                 ; drive doesn't matter
        getdarg es, di, _findAccess                     ; point to find access
        lea di, offset findFileName [ di ]              ; resultant filename
        mov cx, (sizeTempExpandedFCBNAME - 2)
        rep movsb                                       ; copy effectively

        clc
        jmp short locByAttrib_56

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  error exit
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locByAttrib_54:
        mov ax, offset pexterrNoMoreFiles
        stc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locByAttrib_56:
        restoreSegments
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Return Where in Directory                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ss:di  pointer to dir access block                          ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   dx:ax  directory sector                                     ;
        ;   cl     offset in items from start of sector                 ;
        ;                                                               ;
        ;  Assumes ss == ds                                             ;
        ;...............................................................;

getWhereInDir:

        mov ax, word ptr [ fileAcDirOffset ][ di ]
        sub ax, ccbData                                 ; adjust for header offset
        mov cl, sizeDIRENTRY                            ; bytes per entry
        div cl                                          ; remainder should be zero
        mov cx, ax                                      ; return in cl

        mov ax, word ptr [ fileAcDirSector. _low  ][ di ]
        mov dx, word ptr [ fileAcDirSector. _high ][ di ]
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Blank Init Directory Name                                    ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:si  pointer to dir entry in buffer                       ;
        ;                                                               ;
        ;...............................................................;

blankinitDirName:

        push di
        push cx
        push ax

        mov di, si
        clearMemory sizeFILENAME, '  '

        pop ax
        pop cx 
        pop di
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Scan Directory (Exact Search)                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Note:                                                        ;
        ;    Subdirectories are implemented as files.  That means that  ;
        ;    after each sector we must search for the next FAT table    ;
        ;    entry to get the next cluster.                             ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   dx     cluster of dir to search                             ;
        ;   es:di  filename to search                                   ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   if item found:                                              ;
        ;   ax     drive                                                ;
        ;   bx     attributes                                           ;
        ;   cx     file size                                            ;
        ;   dx     cluster                                              ;
        ;   es:si  pointer to located directory entry                   ;
        ;                                                               ;
        ;   if item NOT found:                                          ;
        ;   cy     if item not found                                    ;
        ;...............................................................;

scanDirectory:

        Entry
        ddef _filenamePtr, es, di
        defbytes _diskAccess, sizeDISKACCESS

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init access 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        setES ss
        lea bx, offset _diskAccess [ bp ]               ; pointer to access block
        call initdiskAccess                             ; [ax] is drive, [dx] is cluster
        call getDPB                                     ; check for media change

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  lookup entry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        setES ss
        mov word ptr [ _diskAccess. diskAcPosition. _low  ][ bp ], -sizeDIRENTRY
        mov word ptr [ _diskAccess. diskAcPosition. _high ][ bp ], -1
        mov word ptr [ _diskAccess. diskAcOptions         ][ bp ], (ccb_isDIR)

scanDir_36:
        add word ptr [ _diskAccess. diskAcPosition. _low  ][ bp ], sizeDIRENTRY
        adc word ptr [ _diskAccess. diskAcPosition. _high ][ bp ], 0000
        lea bx, offset _diskAccess [ bp ]               ; pointer to access block
        call _FATReadRandom                             ; read into buffer
        stc                                             ; just in case error,
        jz  scanDir_56                                  ; if no more data -->

        lea di, offset deName[ bx ]                     ; get pointer to name
        cmp byte ptr es:[ di ], DIRENTRY_NEVERUSED
        stc                                             ; just in case error,
        jz  scanDir_56                                  ; if no more data -->

        test byte ptr es:[ deAttributes   ][ bx ], ATTR_VOLUME
        jnz scanDir_36                                  ; scan skips volume entries -->

        lds si, dword ptr [ _filenamePtr ][ bp ]        ; pointer to search file name
        call compareDirEntries
        jnz scanDir_36                                  ; if item not found -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  item found
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov si, bx                                      ; dir entry to si
        mov dx, word ptr es:[ deStartCluster ][ si ]    ; cluster
        mov cx, word ptr es:[ deFileSize     ][ si ]    ; file size
        mov bl, byte ptr es:[ deAttributes   ][ si ]    ; attributes
        xor bh, bh                                      ; zero, nc

        mov ax, word ptr [ _diskAccess. diskAcDrive ][ bp ]

scanDir_56:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Compare Entries                                              ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Compare two directory entries.  If the Lead character of     ;
        ;  source string is E5, it is converted to 05 to help search.   ;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ds:si  source string (may contain ? character )             ;
        ;   es:di  match string (may not contain wild character )       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   zr     entry located                                        ;
        ;...............................................................;

compareDirEntries:

        cmp byte ptr [ si ], DIRENTRY_DELETED           ; E5 ?
        jnz compareDirEntries_04
        mov byte ptr [ si ], SPECIAL_CHAR

compareDirEntries_04:
        cmp byte ptr es:[ di ], DIRENTRY_NEVERUSED
        jz compareDirEntries_08
        cmp byte ptr es:[ di ], DIRENTRY_DELETED
        jnz compareDirEntries_12

compareDirEntries_08:
        mov al, byte ptr es:[ di ]
        cmp al, '.'                                     ; force non zero
        ret

compareDirEntries_12:
        push di
        push si
        mov cx, sizeFILENAME
        call CompareString                              ; source / dest compare
        pop si
        pop di                                          ; restore pointers
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Locate Empty Slot in Directory                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Note:                                                        ;
        ;    This will allocate space to any  subdirectory  except the  ;
        ;    root directory  itself  in  order  to  create  additional  ;
        ;    directory entries.                                         ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ax     drive                                                ;
        ;   dx     cluster of dir to search                             ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ax     drive                                                ;
        ;   es:si  pointer to located directory entry                   ;
        ;   cy     if item not found                                    ;
        ;...............................................................;

LocateFreeDirSlot:

        Entry
        defbytes _diskAccess, sizeDISKACCESS

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  init access 
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        setES ds
        lea bx, offset _diskAccess [ bp ]               ; pointer to access block
        call initdiskAccess                             ; [ax] is drive, [dx] is cluster
        call getAddrDPB                                 ; see if drive initialized

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  search for empty entry
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        mov word ptr [ _diskAccess. diskAcPosition. _low  ][ bp ], -sizeDIRENTRY
        mov word ptr [ _diskAccess. diskAcPosition. _high ][ bp ], -1  
        mov word ptr [ _diskAccess. diskAcOptions         ][ bp ], (ccb_isDIR)

locateFreeDirSlot_12:
        add word ptr [ _diskAccess. diskAcPosition. _low  ][ bp ], sizeDIRENTRY
        adc word ptr [ _diskAccess. diskAcPosition. _high ][ bp ], 0000
        lea bx, offset _diskAccess [ bp ]               ; pointer to access block
        call _FATReadRandom                             ; read into buffer
        stc                                             ; just in case error,
        jz  locateFreeDirSlot_18                        ; if no more data -->

        cmp byte ptr es:[ deName ][ bx ], DIRENTRY_NEVERUSED
        jz locateFreeDirSlot_16
        cmp byte ptr es:[ deName ][ bx ], DIRENTRY_DELETED
        jnz locateFreeDirSlot_12                        ; if item not found -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  item found
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFreeDirSlot_16:
        mov si, bx                                      ; dir entry to si
        or si, si                                       ; no carry
        Return

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if not root directory, append a cluster
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFreeDirSlot_18:
        cmp word ptr [ _diskAccess. diskAcBegCluster ][ bp ], 0000
        stc                                             ; just in case error,
        jz locateFreeDirSlot_28                         ; if root dir, can't extend -->

        mov ax, word ptr [ _diskAccess. diskAcDrive      ][ bp ]
        mov dx, word ptr [ _diskAccess. diskAcCurCluster ][ bp ]
        call AllocateInitCluster                        ; init a cluster
        jc locateFreeDirSlot_28                         ; if can't append -->

        mov cx, dx                                      ; cluster address to update
        mov ax, word ptr [ _diskAccess. diskAcDrive      ][ bp ]
        mov dx, word ptr [ _diskAccess. diskAcCurCluster ][ bp ]
        call updateClusterValue
        lea si, offset ccbData [ di ]                   ; dir address to di
        or si, si                                       ; no carry

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

locateFreeDirSlot_28:
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Actual (Current Drive as a = 0, b = 1, ... )             ;
        ;...............................................................;

GetActualDrive:

        Entry 1
        Arg _drive

        push ds
        currSegment ds
        mov al, byte ptr [ _drive ][ bp ]
        and ax, 001fh
        dec al
        jge GetActualDrive_20
        mov al, byte ptr [ _RxDOS_CurrentDrive ]

GetActualDrive_20:
        mov dx, ax
        cmp al, byte ptr [ _RxDOS_bLastDrive ]
        jnc GetActualDrive_Error
        clc

GetActualDrive_Return:
        pop ds
        Return

GetActualDrive_Error:
        stc
        mov ax, offset pexterrInvalidDrive
        jmp GetActualDrive_Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Device from String                                       ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:si  input string                                         ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:bx  pointer to device header                             ;
        ;   cy     device not found                                     ;
        ;...............................................................;

getDevice:

        Entry
        defbytes _devicename, sizeTempFILENAME

        push ds                                         ; push these registers
        push di
        push si

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  get device name, if any.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        setDS es
        setES ss
        lea di, offset _devicename [ bp ]
        mov cx, sizeTempFILENAME - 1
        xor ax, ax

getDevice_08:
        lodsb
        cmp al, ' '+1                                   ; if space or control
        jc getDevice_12
        cmp al, '/'                                     ; if slash
        jz getDevice_12
        cmp al, '\'                                     ; if backslash
        jz getDevice_12
        cmp al, '.'                                     ; if extension separator
        jz getDevice_12
        cmp al, ':'                                     ; if colon break
        jz getDevice_12

        call upperCase
        stosb                                           ; store name
        loop getDevice_08                               ; continue -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  see if name is legitimate
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

getDevice_12:
        mov byte ptr es:[ di ], 0                       ; zero terminate string
        currSegment ds                                  ; restore ds:
        lea di, offset _devicename [ bp ]
        call checkforDeviceName                         ; see if char device name
        jc getDevice_26                                 ; if invalid -->

        mov ax, es
        or ax, ax                                       ; nc character device 
        jmp short getDevice_Return                      ; exit with bad device error -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  if error
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

getDevice_26:
        mov ax, offset pexterrIllegalName
        stc

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

getDevice_Return:
        push si
        push di
        push ds                                         ; restore these registers
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Drive from String                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   es:si  input string                                         ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:si  past ':', is any.                                    ;
        ;   ax     current drive or drive named.                        ;
        ;   cy     error, invalid drive.                                ;
        ;...............................................................;

getDrive:

        mov ax, word ptr es:[ si ]                      ; save drive info available
        cmp ah, ':'                                     ; drive break in string ?
        jnz getDrive_14                                 ; no, return current -->

        xor ah, ah                                      ; clear carry
        call upperCase                                  ; convert drive to upper case
        sub al, 'A'                                     ; convert to a range
        jc getDrive_12                                  ; if invalid, return valid drive with carry ->

        cmp al, byte ptr ss:[ _RxDOS_bLastDrive ]       ; within valid range ?
        jge getDrive_12                                 ; yes -->

        add si, 2                                       ; skip drive letter / colon

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return valid drive.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        cbw                                             ; valid drive
        or ax, ax
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return valid drive.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

getDrive_12:
        stc
        mov al, byte ptr ss:[ _RxDOS_CurrentDrive ]
        ret

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  return default drive.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

getDrive_14:
        xor ax, ax                                      ;clear carry
        mov al, byte ptr ss:[ _RxDOS_CurrentDrive ]
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Get Current Directory Cluster                                ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   al     drive                                                ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   es:bx  points to cds                                        ;
        ;   dx     starting cluster address                             ;
        ;...............................................................;

getCurrDirCluster:

        push ax
        push cx

        mov cl, sizeCDS
        mul cl                                          ; offset into cds table

        les bx, dword ptr ss:[ _RxDOS_pCDS ]            ; CDS start address
        add bx, ax                                      ; offset to proper drive
        mov dx, word ptr es:[ _cdsStartClusterDir ][ bx ]

        pop cx
        pop ax
        ret

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read To App Buffer                                           ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ss:bx  Access Control Buffer                                ;
        ;   es:di  buffer address                                       ;
        ;   dx:ax  file position to start reading                       ;
        ;   cx     bytes to read                                        ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ss:bx  Access Control Buffer                                ;
        ;   es:di  buffer address                                       ;
        ;   cx     bytes actually read                                  ;
        ;   zr     means end of file or wrong address                   ;
        ;                                                               ;
        ;  Preserved:                                                   ;
        ;   All registers except CX and FLAGS                           ;
        ;                                                               ;
        ;...............................................................;

readLogicalBuffer:

        Entry
        ddef _accessControl, ss, bx
        ddef _readbuffer
        def  _bytestoRead, cx
        def  _bytesRead, 0000

        SaveSegments di, si, dx, bx, ax
        mov word ptr ss:[ diskAcPosition. _low  ][ bx ], ax
        mov word ptr ss:[ diskAcPosition. _high ][ bx ], dx  

readLogicalBuffer_08:
        NormalizeBuffer es, di                          ; normalize buffer pointer
        stordarg _readbuffer, es, di                    ; save read buffer pointer

        and word ptr ss:[ diskAcOptions ][ bx ], NOT (DISKAC_READBUFFER_ASSIGNED + DISKAC_READ_MULTIPLESECTORS)
        mov cx, word ptr [ _bytestoRead ][ bp ]         ; get length remaining
        cmp cx, sizeSector + 1                          ; greater than sector size ?
        jc readLogicalBuffer_12                         ; no -->

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; optimize read setup
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        getarg bx, _accessControl                       ; make sure we point to access block
        or word ptr ss:[ diskAcOptions ][ bx ], (DISKAC_READBUFFER_ASSIGNED + DISKAC_READ_MULTIPLESECTORS)
        mov word ptr ss:[ diskAcBytesToRead ][ bx ], cx

        mov word ptr ss:[ diskAcBufferPtr. _segment ][ bx ], es
        mov word ptr ss:[ diskAcBufferPtr. _pointer ][ bx ], di

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; actual read
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

readLogicalBuffer_12:
        getarg bx, _accessControl                       ; make sure we point to access block
        or word ptr ss:[ diskAcOptions ][ bx ], (ccb_isDATA)
        call _FATReadRandom                             ; get buffer, read at position
        or cx, cx                                       ; any bytes read ?
        jz readLogicalBuffer_24                         ; no -->

     ; es:bx, points to position in read buffer 
     ; cx, # characters read

        getarg si, _accessControl                       ; make sure we point to access block
        test word ptr ss:[ diskAcOptions ][ si ], DISKAC_RESULT_READMULTIPLESECTORS
        jnz readLogicalBuffer_20                         ; if buffered read -->

        cmp cx, word ptr [ _bytestoRead ][ bp ]         ; compare against length needed
        jc readLogicalBuffer_16                         ; if less are available -->
        mov cx, word ptr [ _bytestoRead ][ bp ]         ; minimize

readLogicalBuffer_16:
        push ds
        push cx
        setDS es
        mov si, bx                                      ; where data read [ds: si]
        les di, dword ptr [ _readbuffer ][ bp ]         ; to destination  [es: di]
        fastmove si, di                                 ; move buffer

        pop cx
        pop ds

readLogicalBuffer_20:
        getarg bx, _accessControl                       ; make sure we point to access block
        add word ptr ss:[ diskAcPosition. _low  ][ bx ], cx
        adc word ptr ss:[ diskAcPosition. _high ][ bx ], 0000
        getdarg es, di, _readbuffer                     ; read buffer pointer
        add di, cx                                      ; position beyond buffer

        add word ptr [ _bytesRead ][ bp ], cx
        sub word ptr [ _bytestoRead ][ bp ], cx         ; compare against length needed
        jc readLogicalBuffer_24                         ; if no more to read -->
        jz readLogicalBuffer_24                         ; if no more to read -->
        jmp readLogicalBuffer_08                        ; continue reading -->

readLogicalBuffer_24:
        mov cx, word ptr [ _bytesRead ][ bp ]

        RestoreSegments ax, bx, dx, si, di
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Read Line To App Buffer                                      ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ss:bx  Access Control Buffer                                ;
        ;   es:di  buffer address                                       ;
        ;   cx     max bytes to read                                    ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ss:bx  Access Control Buffer                                ;
        ;   es:di  buffer address                                       ;
        ;   ax:dx  position in file after last byte read                ;
        ;   cx     bytes returned, excluding trailing cr/lf             ;
        ;   zr     means end of file or wrong address                   ;
        ;...............................................................;

readLine:

        Entry
        ddef _accessControl, ss, bx
        ddef _readbuffer, es, di
        def  _bytestoRead, cx
        def  _bytesRead, 0000
        def  _bytesReturned, 0000                       ; line feeds removed
        ddef _bufferPtr

        SaveSegments di, si, bx

readLine_08:
        currSegment ds
        NormalizeBuffer es, di                          ; normalize buffer pointer
        stordarg _readbuffer, es, di                    ; save read buffer pointer

readLine_12:
        getarg bx, _accessControl                       ; make sure we point to access block
        mov ax, word ptr ss:[ diskAcFileSize. _low  ][ bx ]
        mov dx, word ptr ss:[ diskAcFileSize. _high ][ bx ]
        sub ax, word ptr ss:[ diskAcPosition. _low  ][ bx ]
        sbb dx, word ptr ss:[ diskAcPosition. _high ][ bx ]
        jc readLine_14                                  ; if past eof ->
        or dx, ax                                       ; at end of file ?
        jz readLine_14                                  ; or if at end of file ->

        mov word ptr ss:[ diskAcOptions ][ bx ], (ccb_isDATA)
        call _FATReadRandom                             ; get buffer, read at position
        stordarg _bufferPtr, es, bx

        or cx, cx                                       ; any bytes left ?
        jnz readLine_16                                 ; yes -->

readLine_14:
        cmp word ptr [ _bytesReturned ][ bp ], 0000     ; any data returned ?
        jnz readLine_66                                 ; yes, do valid return -->

        xor cx, cx
        jmp readLine_68                                 ; else  return with Zero

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
; parse through buffer
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
readLine_16:
        getarg bx, _accessControl                       ; make sure we point to access block
        mov ax, word ptr ss:[ diskAcFileSize. _low  ][ bx ]
        mov dx, word ptr ss:[ diskAcFileSize. _high ][ bx ]
        sub ax, word ptr ss:[ diskAcPosition. _low  ][ bx ]
        sbb dx, word ptr ss:[ diskAcPosition. _high ][ bx ]
        or dx, dx                                       ; if lots of space left to read 
        jnz readLine_20                                 ; then use all allowable cx -->

        or ax, ax                                       ; any bytes left ?
        jz readLine_14                                  ; no -->

        cmp ax, cx                                      ; space left less than returned ?
        jnc readLine_20                                 ; no -->
        mov cx, ax                                      ; use only till end of file

readLine_20:
        cmp cx, word ptr [ _bytestoRead ][ bp ]         ; returned more than can read ?
        jc readLine_22                                  ; no -->
        mov cx, word ptr [ _bytestoRead ][ bp ]         ; max allowed

readLine_22:
        push cx
        getdarg ds, si, _bufferPtr                      ; where data read [ds: si]
        getdarg es, di, _readbuffer                     ; to destination  [es: di]

readLine_36:
        lodsb
        cmp al, 'J'-40h                                 ; line feed ?
        jz readLine_38                                  ; yes, drop lf's -->
        cmp al, 'M'-40h                                 ; carriage return ?
        jz readLine_44                                  ; yes -->

        stosb                                           ; save character
        inc word ptr [ _bytesReturned ][ bp ]           ; how many actually returned

readLine_38:
        loop readLine_36                                ; continue -->

        pop cx
        add word ptr [ _bytesRead ][ bp ], cx           ; how many actually read
        sub word ptr [ _bytestoRead ][ bp ], cx         ; compare against length needed
        jc readLine_66                                  ; if no more to read -->
        jz readLine_66                                  ; if no more to read -->

        getarg bx, _accessControl                       ; make sure we point to access block
        add word ptr ss:[ diskAcPosition. _low  ][ bx ], cx
        adc word ptr ss:[ diskAcPosition. _high ][ bx ], 0000
        getdarg es, di, _readbuffer                     ; read buffer pointer
        add di, cx                                      ; position beyond buffer
        jmp readLine_12                                 ; continue reading -->

readLine_44:
        xor al, al
        stosb                                           ; save null terminating character
     ;  inc word ptr [ _bytesReturned ][ bp ]           ; how many actually returned

        dec cx                                          ; account for line feed
        pop ax                                          ; bytes expected
        sub ax, cx                                      ; bytes actually read
        add word ptr [ _bytesRead ][ bp ], ax           ; how many actually read

        getarg bx, _accessControl                       ; make sure we point to access block
        add word ptr ss:[ diskAcPosition. _low  ][ bx ], ax
        adc word ptr ss:[ diskAcPosition. _high ][ bx ], 0000

readLine_66:
        mov al, -1
        or ax, ax                                       ; non-zero retun if data

readLine_68:
        mov cx, word ptr [ _bytesReturned ][ bp ]       ; how many actually returned
        mov ax, word ptr ss:[ diskAcPosition. _low  ][ bx ]
        mov dx, word ptr ss:[ diskAcPosition. _high ][ bx ]

        RestoreSegments bx, si, di
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Write From App Buffer                                        ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Note:                                                        ;
        ;                                                               ;
        ;  Write uses FATRead to compute the  correct  logical  sector  ;
        ;  from cluster information.   If  a sector cannot be located,  ;
        ;  then we attach a new cluster to the end of the file.         ;
        ;                                                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ss:bx  Access Control Buffer                                ;
        ;   es:di  buffer address                                       ;
        ;   dx:ax  file position to start writing                       ;
        ;   cx     bytes to write                                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ss:bx  Access Control Buffer                                ;
        ;   es:di  buffer address                                       ;
        ;   cx     bytes actually written                               ;
        ;   zr     means end of file or wrong address                   ;
        ;...............................................................;

writeLogicalBuffer:

        Entry
        ddef _accessControl, ss, bx
        ddef _appBuffer
        def  _bytestoWrite, cx
        def  _bytesWritten, 0000

        SaveSegments di, si, dx, bx, ax
        mov word ptr ss:[ diskAcPosition. _low  ][ bx ], ax
        mov word ptr ss:[ diskAcPosition. _high ][ bx ], dx  
        mov word ptr ss:[ diskAcOptions  ][ bx ], DISKAC_OPTIMIZEDWRITE + ccb_isDATA

writeLogicalBuffer_08:
        NormalizeBuffer es, di                          ; normalize buffer pointer
        stordarg _appbuffer, es, di                     ; save app buffer pointer

        cmp word ptr [ _bytestoWrite ][ bp ], 0000      ; write none ?
        jz writeLogicalBuffer_32                        ; yes -->

writeLogicalBuffer_10:
        getarg bx, _accessControl                       ; make sure we point to access block
        call _FATReadRandom                             ; get buffer, read at position
        or cx, cx                                       ; any bytes left ?
        jnz writeLogicalBuffer_14                       ; yes, no need to alloc more -->

        getarg bx, _accessControl                       ; make sure we point to access block
        mov ax, word ptr ss:[ diskAcDrive      ][ bx ]
        mov dx, word ptr ss:[ diskAcCurCluster ][ bx ]
        call AppendCluster                              ; else, append a cluster
        jnc writeLogicalBuffer_10                       ; go try to link to it now -->

        mov word ptr [ _bytesWritten ][ bp ], 0000      ; BUG: This is here untilwe figure something better
        jmp short writeLogicalBuffer_32                 ; go try to link to it now -->

writeLogicalBuffer_14:
        cmp cx, word ptr [ _bytestoWrite ][ bp ]        ; compare against length needed
        jc writeLogicalBuffer_16                        ; if less are available -->
        mov cx, word ptr [ _bytestoWrite ][ bp ]        ; maximize

writeLogicalBuffer_16:
        or cx, cx                                       ; more to write
        jz writeLogicalBuffer_32                        ; no -->

        push ds
        push cx                                         ; save bytes written in move
        mov di, bx                                      ; where to write data [es: di]
        lds si, dword ptr [ _appbuffer ][ bp ]          ; data source [ds: si]
        fastmove si, di                                 ; move buffer

        mov si, bx                                      ; where data written
        call locateCCBPHeader                           ; find ccb header
        call CCBChanged                                 ; mark block changed 

        pop cx
        pop ds
        getarg bx, _accessControl                       ; make sure we point to access block
        call adjustPositionAndFileSize                  ; bytes read in cx

        getdarg es, di, _appbuffer                      ; app buffer pointer
        add di, cx                                      ; position beyond buffer

        add word ptr [ _bytesWritten ][ bp ], cx
        sub word ptr [ _bytestoWrite ][ bp ], cx        ; compare against length needed
        jc writeLogicalBuffer_32                        ; if no more to write -->
        jz writeLogicalBuffer_32                        ; if no more to write -->
        jmp writeLogicalBuffer_08                       ; continue writing -->

writeLogicalBuffer_32:
        call updateAllChangedCCBBuffers
        mov cx, word ptr [ _bytesWritten ][ bp ]

        RestoreSegments ax, bx, dx, si, di
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Fill Space Required with NULLS                               ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ss:bx  Access Control Buffer                                ;
        ;   dx:ax  bytes to fill with nulls                             ;
        ;   stack  file position to start writing                       ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   ss:bx  Access Control Buffer                                ;
        ;   cx     bytes actually written                               ;
        ;   zr     means end of file or wrong address                   ;
        ;...............................................................;

fillLogicalBuffer:

        Entry 2
        darg _position

        ddef _accessControl, ss, bx
        ddef _bytestoFill, dx, ax                       ; bytes to fill
        ddef _bytesFilled                               ; bytes filled (returned)

        SaveSegments di, si, bx
        xor ax, ax
        stordarg _bytesFilled, ax, ax                   ; bytes filled (returned)

        getdarg dx, ax, _position
        mov word ptr ss:[ diskAcPosition. _high ][ bx ], dx  
        mov word ptr ss:[ diskAcPosition. _low  ][ bx ], ax
        mov word ptr ss:[ diskAcOptions         ][ bx ], (ccb_isDATA)

fillLogicalBuffer_08:
        getarg bx, _accessControl                       ; make sure we point to access block
        call _FATReadRandom                             ; get buffer, read at position
        or cx, cx                                       ; past end of allocation ?
        jnz fillLogicalBuffer_10                        ; no, no need to append a cluster -->

        getarg bx, _accessControl                       ; make sure we point to access block
        mov ax, word ptr ss:[ diskAcDrive      ][ bx ]
        mov dx, word ptr ss:[ diskAcCurCluster ][ bx ]
        call AppendCluster                              ; else, append a cluster
        jnc fillLogicalBuffer_08                        ; if successful, go access next -->
        jmp short fillLogicalBuffer_34                  ; else error -->

fillLogicalBuffer_10:
        mov ax, word ptr [ _bytestoFill. _low  ][ bp ]
        mov dx, word ptr [ _bytestoFill. _high ][ bp ]
        sub ax, cx
        sbb dx, 0000
        jnc fillLogicalBuffer_12                        ; if more bytes to fill than available -->
        mov cx, word ptr [ _bytestoFill. _low  ][ bp ]

;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;  MS-DOS doesn't NULL fill expanded areas, so this code is disabled, 
;  but FAT allocations still take place.
;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

fillLogicalBuffer_12:
;       push ds
;       push cx
;       mov di, bx                                      ; where to write data [es: di]
;       xor ax, ax                                      ; fill with NULLS
;       shr cx, 1                                       ; maximize by filling words
;       rep stosw
;       jnc fillLogicalBuffer_16                        ; if even fill ->
;       stosb                                           ; last byte.

fillLogicalBuffer_16:
;       mov si, bx                                      ; where data written
;       call locateCCBPHeader                           ; find ccb header
;       call CCBChanged                                 ; mark block changed 
;
;       pop cx
;       pop ds
        add word ptr [ _bytesFilled. _low  ][ bp ], cx
        adc word ptr [ _bytesFilled. _high ][ bp ], 0000
        
        mov ax, word ptr [ _bytestoFill. _low  ][ bp ]
        mov dx, word ptr [ _bytestoFill. _high ][ bp ]
        sub ax, cx
        sbb dx, 0000
        jc fillLogicalBuffer_32                         ; if no more to fill -->

        mov word ptr [ _bytestoFill. _low  ][ bp ], ax
        mov word ptr [ _bytestoFill. _high ][ bp ], dx
        or ax, dx                                       ; at end of write ?
        jz fillLogicalBuffer_32                         ; if no more to fill -->

        getarg bx, _accessControl                       ; make sure we point to access block
        call adjustPositionAndFileSize                  ; bytes moved in cx
        jmp fillLogicalBuffer_08                        ; continue filling -->

fillLogicalBuffer_32:
        or ax, ax                                       ; clear error flag

fillLogicalBuffer_34:
        call updateAllChangedCCBBuffers

        getdarg dx, ax, _bytesFilled
        RestoreSegments bx, si, di
        Return

        ;''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''';
        ;  Adjust FileSize in Disk Access Block                         ;
        ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -;
        ;                                                               ;
        ;  Input:                                                       ;
        ;   ss:bx  Access Control Buffer                                ;
        ;   cx     bytes read                                           ;
        ;                                                               ;
        ;  Output:                                                      ;
        ;   dx:ax  Disk Access Position                                 ;
        ;                                                               ;
        ;...............................................................;

adjustPositionAndFileSize:
       
        add word ptr ss:[ diskAcPosition. _low  ][ bx ], cx
        adc word ptr ss:[ diskAcPosition. _high ][ bx ], 0000

        mov ax, word ptr ss:[ diskAcPosition. _low  ][ bx ]
        mov dx, word ptr ss:[ diskAcPosition. _high ][ bx ]
        sub ax, word ptr ss:[ diskAcFileSize. _low  ][ bx ]
        sbb dx, word ptr ss:[ diskAcFileSize. _high ][ bx ]

        mov ax, word ptr ss:[ diskAcPosition. _low  ][ bx ]
        mov dx, word ptr ss:[ diskAcPosition. _high ][ bx ]
        jc adjustPositionAndFileSize_08

        mov word ptr ss:[ diskAcFileSize. _low  ][ bx ], ax
        mov word ptr ss:[ diskAcFileSize. _high ][ bx ], dx

adjustPositionAndFileSize_08:
        ret

RxDOS   ENDS
        END

