        TITLE   FCBIO2 - FCB system calls
        NAME    FCBIO2

IF 1
__TONYI__ = 1	; Space/Speed hacks
__TONYI__1= 1	; Space/Speed hacks  10/28/92
__TONYI__2= 1	; Space/Speed hacks  10/28/92
ENDIF

;
;       Microsoft Confidential
;       Copyright (C) Microsoft Corporation 1991
;       All Rights Reserved.
;
;**     FCBIO2.ASM - Ancient 1.0 1.1 FCB system calls
;
;       GetRR
;       GetExtent
;       SetExtent
;       GetExtended
;       GetRecSize
;       FCBIO
;       $FCB_OPEN
;       $FCB_CREATE
;       $FCB_RANDOM_WRITE_BLOCK
;       $FCB_RANDOM_READ_BLOCK
;       $FCB_SEQ_READ
;       $FCB_SEQ_WRITE
;       $FCB_RANDOM_READ
;       $FCB_RANDOM_WRITE
;
;       Revision history:
;
;               Created: ARR 4 April 1983
;                        MZ  6 June  1983 completion of functions
;                        MZ 15 Dec   1983 Brain damaged programs close FCBs multiple
;                                 times.  Change so successive closes work by
;                                 always returning OK.  Also, detect I/O to
;                                 already closed FCB and return EOF.
;                MZ 16 Jan   1984 More braindamage.  Need to separate info
;                                 out of sft into FCB for reconnection
;
;           A000   version 4.00 Jan. 1988
;
;         06/02/92 - B2560 - Invalid error flag set for disk full     ;B2560;
;                             problem when using FCB's.  Removed one  ;B2560;
;                             line of code.  PTR number is B732560.   ;B2560;
;                             WRA.                                    ;B2560;
;                                                                     ;AC000;
;    06/12/92 - AC000 - Changed long Jump to SHORT.  WRA.             ;AC000;
;                                                                     ;AC000;
;
;

        .xlist
        .xcref
        include version.inc
        include dosseg.inc
        INCLUDE DOSSYM.INC
        INCLUDE DEVSYM.INC
        include sf.inc
        include cpmfcb.inc
        include filemode.inc
        include bugtyp.inc
        .cref
        .list


        I_need  DMAAdd,DWORD            ; current user's DMA address
        I_need  OpenBuf,128             ; buffer for translating paths
        I_need  ThisSFT,DWORD           ; SFT in use
        I_need  sftFCB,DWORD            ; pointer to SFTs for FCB cache
        I_need  FCBLRU,WORD             ; least recently used count
        I_need  DISK_FULL,BYTE          ; flag for disk full

        I_need  LocalSFT,DWORD          ;Cache for new FCB implementation

; Defintions for FCBOp flags

Random  =   2                           ; random operation
FCBRead =   4                           ; doing a read
Block   =   8                           ; doing a block I/O


DOSCODE SEGMENT
        ASSUME  SS:DOSDATA,CS:DOSCODE


        EXTRN   DOS_Read:NEAR, DOS_Write:NEAR
        EXTRN   DOS_Open:NEAR, DOS_Create:NEAR


 Break <GetRR - return the random record field in DX:AX>
;---------------------------------------------------------------------------
;
;
;   GetRR - correctly load DX:AX with the random record field (3 or 4 bytes)
;       from the FCB pointed to by DS:SI
;
;   Inputs:     DS:SI point to an FCB
;               BX has record size
;   Outputs:    DX:AX contain the contents of the random record field
;   Registers modified: none
;---------------------------------------------------------------------------

Procedure   GetRR,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     AX,WORD PTR [SI.FCB_RR] ; get low order part
        MOV     DX,WORD PTR [SI.FCB_RR+2]   ; get high order part
        CMP     BX,64                   ; ignore MSB of RR if recsiz > 64
        JB      GetRRBye
IFDEF __TONYI__1
;----------------------------------------------------
; Exit merge point...
;----------------------------------------------------
Merge_XOR_DH_DH_ret:
ENDIF
        XOR     DH,DH
GetRRBye:
        return
EndProc GetRR

Break <GetExtent - retrieve next location for sequential IO>
;---------------------------------------------------------------------------
;
;   GetExtent - Construct the next record to perform I/O from the EXTENT and
;       NR fields in the FCB.
;
;   Inputs:     DS:SI - point to FCB
;   Outputs:    DX:AX contain the contents of the random record field
;   Registers modified: none
;---------------------------------------------------------------------------

Procedure   GetExtent,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     AL,[SI.fcb_NR]          ; get low order piece
        MOV     DX,[SI.fcb_EXTENT]      ; get high order piece
        SHL     AL,1
        SHR     DX,1
        RCR     AL,1                    ; move low order bit of DL to high order of AH
        MOV     AH,DL
        MOV     DL,DH
IFDEF __TONYI__1
;----------------------------------------------------
; Save a byte.......
;----------------------------------------------------
	jmp	short Merge_XOR_DH_DH_ret
ELSE
        XOR     DH,DH
        return
ENDIF
EndProc GetExtent

Break <SetExtent - update the extent/NR field>
;---------------------------------------------------------------------------
;
;   SetExtent - change the position of an FCB by filling in the extent/NR
;       fields
;
;   Inputs:     DS:SI point to FCB
;               DX:AX is a record location in file
;   Outputs:    Extent/NR fields are filled in
;   Registers modified: CX
;---------------------------------------------------------------------------

Procedure SetExtent,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        SAVE    <AX,DX>
        MOV     CX,AX
        AND     AL,7FH                  ; next rec field
        MOV     [SI.fcb_NR],AL
        AND     CL,80H                  ; save upper bit
        SHL     CX,1
        RCL     DX,1                    ; move high bit of CX to low bit of DX
        MOV     AL,CH
        MOV     AH,DL
        MOV     [SI.fcb_EXTENT],AX      ; all done
        RESTORE <DX,AX>
        return
EndProc SetExtent

Break <GetExtended - find FCB in potential extended fcb>
;---------------------------------------------------------------------------
;
;   GetExtended - Make DS:SI point to FCB from DS:DX
;
;   Inputs:     DS:DX point to a possible extended FCB
;   Outputs:    DS:SI point to the FCB part
;               zeroflag set if not extended fcb
;   Registers modified: SI
;---------------------------------------------------------------------------

Procedure   GetExtended,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     SI,DX                   ; point to Something
        CMP     BYTE PTR DS:[SI],-1     ; look for extention
        JNZ     GetBye                  ; not there
        ADD     SI,7                    ; point to FCB
GetBye:
        CMP     SI,DX                   ; set condition codes
        return
EndProc GetExtended

Break <GetRecSize - return in BX the FCB record size>
;---------------------------------------------------------------------------
;
;   GetRecSize - return in BX the record size from the FCB at DS:SI
;
;   Inputs:     DS:SI point to a non-extended FCB
;   Outputs:    BX contains the record size
;   Registers modified: None
;---------------------------------------------------------------------------

Procedure   GetRecSize,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     BX,[SI.fcb_RECSIZ]      ; get his record size
        OR      BX,BX                   ; is it nul?
        retnz
IFDEF __TONYI__
	mov	bl, 128	; Since BX is known to be 0 we can save a byte
ELSE
        MOV     BX,128                  ; use default size
ENDIF
        MOV     [SI.fcb_RECSIZ],BX      ; stuff it back
        return
EndProc GetRecSize

IFDEF __TONYI__
;----------------------------------------
; Save some space by allowing SHORT jumps
; to FCBIO (and the last one just falls 
; through to FCBIO.
;----------------------------------------

BREAK <$FCB_Random_write_Block - write a block of records to a file >
;----------------------------------------------------------------------------
;
;   $FCB_Random_Write_Block - retrieve a location from the FCB, seek to it
;	and write a number of blocks from it.
;
;   Inputs:	DS:DX point to an FCB
;   Outputs:	AL = 0 write was successful and the FCB position is updated
;		AL <> 0 Not enough room on disk for the output
;
;----------------------------------------------------------------------------

Procedure $FCB_Random_Write_Block,NEAR

	ASSUME	CS:DOSCODE,SS:DOSDATA
	MOV	AL,Random+Block
	JMP	short FCBIO

EndProc $FCB_Random_Write_Block

BREAK <$FCB_Random_Read_Block - read a block of records to a file >
;----------------------------------------------------------------------------
;
;   $FCB_Random_Read_Block - retrieve a location from the FCB, seek to it
;	and read a number of blocks from it.
;
;   Inputs:	DS:DX point to an FCB
;   Outputs:	AL = error codes defined above
;
;----------------------------------------------------------------------------

Procedure $FCB_Random_Read_Block,NEAR
	ASSUME	CS:DOSCODE,SS:DOSDATA
	MOV	AL,Random+FCBRead+Block
	JMP	short FCBIO
EndProc $FCB_Random_Read_Block

BREAK <$FCB_Seq_Read - read the next record from a file >
;----------------------------------------------------------------------------
;
;   $FCB_Seq_Read - retrieve the next record from an FCB and read it into
;	memory
;
;   Inputs:	DS:DX point to an FCB
;   Outputs:	AL = error codes defined above
;
;----------------------------------------------------------------------------

Procedure $FCB_Seq_Read,NEAR
	ASSUME	CS:DOSCODE,SS:DOSDATA
	MOV	AL,FCBRead
	JMP	short FCBIO
EndProc $FCB_Seq_Read

BREAK <$FCB_Seq_Write - write the next record to a file >
;----------------------------------------------------------------------------
;
;   $FCB_Seq_Write - retrieve the next record from an FCB and write it to the
;	file
;
;   Inputs:	DS:DX point to an FCB
;   Outputs:	AL = error codes defined above
;
;----------------------------------------------------------------------------

Procedure $FCB_Seq_Write,NEAR

	ASSUME	CS:DOSCODE,SS:DOSDATA
	MOV	AL,0
	JMP	short FCBIO

EndProc $FCB_SEQ_WRITE

BREAK <$FCB_Random_Read - Read a single record from a file >
;----------------------------------------------------------------------------
;
;   $FCB_Random_Read - retrieve a location from the FCB, seek to it and read a
;	record from it.
;
;   Inputs:	DS:DX point to an FCB
;   Outputs:	AL = error codes defined above
;
;----------------------------------------------------------------------------

Procedure $FCB_Random_Read,NEAR
	ASSUME	CS:DOSCODE,SS:DOSDATA
	MOV	AL,Random+FCBRead
	JMP	short FCBIO
EndProc $FCB_RANDOM_READ

BREAK <$FCB_Random_Write - write a single record to a file >
;----------------------------------------------------------------------------
;
;   $FCB_Random_Write - retrieve a location from the FCB, seek to it and write
;	a record to it.
;
;   Inputs:	DS:DX point to an FCB
;   Outputs:	AL = error codes defined above
;
;----------------------------------------------------------------------------

Procedure $FCB_Random_Write,NEAR
	ASSUME	CS:DOSCODE,SS:DOSDATA
	MOV	AL,Random
IFNDEF __TONYI__1
;;; *** TONYI *** NOTE!!! This one just falls into FCBIO
	JMP	short FCBIO
ENDIF
EndProc $FCB_RANDOM_WRITE
ENDIF

BREAK <FCBIO - do internal FCB I/O>
;---------------------------------------------------------------------------
;
;   FCBIO - look at FCBOP and merge all FCB operations into a single routine.
;
;   Inputs:     FCBOP flags which operations need to be performed
;               DS:DX point to FCB
;               CX may have count of number of records to xfer
;   Outputs:    AL has error code
;   Registers modified: all
;---------------------------------------------------------------------------

Procedure   FCBIO,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA

PUBLIC FCBIO001S,FCBIO001E
FCBIO001S:
        LocalVar    FCBErr,BYTE
        LocalVar    cRec,WORD
        LocalVar    RecPos,DWORD
        LocalVar    RecSize,WORD
        LocalVar    bPos,DWORD
        LocalVar    cByte,WORD
        LocalVar    cResult,WORD
        LocalVar    cRecRes,WORD
        LocalVar    FCBOp,BYTE
FCBIO001E:
        Enter

FEOF    EQU     1
FTRIM   EQU     2
        MOV     FCBOp,AL
        MOV     FCBErr,0                ;   FCBErr = 0;
        invoke  GetExtended             ;   FCB = GetExtended ();
        TESTB   FCBOp,BLOCK             ;   if ((OP&BLOCK) == 0)
        JNZ     GetPos
        MOV     CX,1                    ;       cRec = 1;
GetPos:
        MOV     cRec,CX                 ;*Tail coalesce
        invoke  GetExtent               ;   RecPos = GetExtent ();
        invoke  GetRecSize              ;   RecSize = GetRecSize ();
        MOV     RecSize,BX
        TESTB   FCBOp,RANDOM            ;   if ((OP&RANDOM) <> 0)
        JZ      GetRec
        invoke  GetRR                   ;       RecPos = GetRR ();
GetRec:
        MOV     RecPosL,AX              ;*Tail coalesce
        MOV     RecPosH,DX
        invoke  SetExtent               ;   SetExtent (RecPos);
        MOV     AX,RecPosH              ;   bPos = RecPos * RecSize;
        MUL     BX
        MOV     DI,AX
        MOV     AX,RecPosL
        MUL     BX
        ADD     DX,DI
        MOV     bPosL,AX
        MOV     bPosH,DX
        MOV     AX,cRec                 ;   cByte = cRec * RecSize;
        MUL     BX
        MOV     cByte,AX

;hkn;   SS override
        ADD     AX,WORD PTR DMAAdd      ;   if (cByte+DMA > 64K) {
        ADC     DX,0
        JZ      DoOper
        MOV     FCBErr,FTRIM            ;       FCBErr = FTRIM;

;hkn;   SS override
        MOV     AX,WORD PTR DMAAdd      ;       cRec = (64K-DMA)/RecSize;
        NEG     AX
        JNZ     DoDiv
        DEC     AX
DoDiv:
        XOR     DX,DX
        DIV     BX
        MOV     cRec,AX
        MUL     BX                      ;       cByte = cRec * RecSize;
        MOV     cByte,AX                ;       }
DoOper:
        XOR     BX,BX
        MOV     cResult,BX              ;   cResult = 0;
        CMP     cByte,BX                ;   if (cByte <> 0 ||
        JNZ     DoGetExt
        TESTB   FCBErr,FTRIM            ;       (FCBErr&FTRIM) == 0) {
        JZ      DoGetExt
IF      DEBUG
        JMP     SkipOp
ELSE
        JMP     short SkipOp
ENDIF

DoGetExt:
        invoke  SFTFromFCB              ;       if (!SFTFromFCB (SFT,FCB))
        JNC     ContinueOp
FCBDeath:
        invoke  FCB_Ret_Err             ; signal error, map for extended
        MOV     cRecRes,0               ; no bytes transferred
        MOV     FCBErr,FEOF             ;           return FTRIM;
        JMP     FCBSave                 ; bam!
ContinueOp:
        Assert  ISSFT,<ES,DI>,"ContinueOP"
IFDEF __TONYI__2
;---------------------------------------------
; Save several bytes....
;---------------------------------------------
	push	ds
	lds	ax, dword ptr [si].fcb_filsiz
	mov	word ptr es:[DI].sf_size,AX
	mov	word ptr es:[DI].sf_size+2,ds
	lds	ax, dword ptr bPos
	mov	dx, ds
	pop	ds
ELSE
        MOV     AX,WORD PTR [SI].fcb_filsiz
        MOV     WORD PTR ES:[DI].sf_size,AX
        MOV     AX,WORD PTR [SI].fcb_filsiz+2
        MOV     WORD PTR ES:[DI].sf_size+2,AX
        MOV     AX,bPosL
        MOV     DX,bPosH
ENDIF
        MOV     WORD PTR ES:[DI.sf_position],AX
        XCHG    WORD PTR ES:[DI.sf_position+2],DX
        PUSH    DX                      ; save away Open age.
        MOV     CX,cByte                ;       cResult =

;hkn; DOS_Read is in DOSCODE
        MOV     DI,OFFSET DOSCODE:DOS_Read ;        *(OP&FCBRead ? DOS_Read
        TESTB   FCBOp,FCBRead           ;                        : DOS_Write)(cRec);
        JNZ     DoContext

;hkn; DOS_Write is in DOSCODE
        MOV     DI,OFFSET DOSCODE:DOS_Write
DoContext:
        SAVE    <BP,DS,SI>

;hkn; SS is DOSDATA
        Context DS
;; Fix for disk full
        CALL    DI
        RESTORE <SI,DS,BP>
        ASSUME  DS:NOTHING
        JC      FCBDeath
        CMP     BYTE PTR [DISK_FULL],0  ; treat disk full as error
        JZ      NODSKFULL
        MOV     BYTE PTR [DISK_FULL],0  ; clear the flag
;       MOV     FCBerr,FEOF             ; set disk full flag          ;B2560;
NODSKFULL:
;; Fix for disk full
        MOV     cResult,CX
        invoke  SaveFCBInfo             ;       SaveFCBInfo (FCB);
        Assert  ISSFT,<ES,DI>,"FCBIO/SaveFCBInfo"
;       BUGBUG - why not just use the SF_OPENAGE symbol?
        .errnz  SF_POSITION+2-SF_OPENAGE
        POP     WORD PTR ES:[DI].sf_Position+2  ; restore open age
IFDEF __TONYI__1
;---------------------------------------------
; Save 3 bytes....
;---------------------------------------------
	push	es
	les	AX,DWORD PTR ES:[DI].sf_size
	MOV	WORD PTR [SI].fcb_filsiz,AX
	MOV	WORD PTR [SI].fcb_filsiz+2,es
	pop	es
ELSE
        MOV     AX,WORD PTR ES:[DI].sf_size
        MOV     WORD PTR [SI].fcb_filsiz,AX
        MOV     AX,WORD PTR ES:[DI].sf_size+2
        MOV     WORD PTR [SI].fcb_filsiz+2,AX
ENDIF

SkipOp:
        MOV     AX,cResult              ;   cRecRes = cResult / RecSize;
        XOR     DX,DX
        DIV     RecSize
        MOV     cRecRes,AX
        ADD     RecPosL,AX              ;   RecPos += cRecResult;
        ADC     RecPosH,0
;
; If we have not gotten the expected number of records, we signal an EOF
; condition.  On input, this is EOF.  On output this is usually disk full.
; BUT...  Under 2.0 and before, all device output IGNORED this condition.  So
; do we.
;
        CMP     AX,cRec                 ;   if (cRecRes <> cRec)
        JZ      TryBlank
        TESTB   FCBOp,FCBRead           ;       if (OP&FCBRead || !DEVICE)
        JNZ     SetEOF
        TESTB   ES:[DI].sf_flags,devid_device
        JNZ     TryBlank
SetEOF:
        MOV     FCBErr,FEOF             ;       FCBErr = FEOF;
TryBlank:                               ;
        OR      DX,DX                   ;   if (cResult%RecSize <> 0) {
        JZ      SetExt
        ADD     RecPosL,1               ;       RecPos++;
        ADC     RecPosH,0
        TESTB   FCBOp,FCBRead           ;       if(OP&FCBRead) <> 0) {
        JZ      SetExt
        INC     cRecRes                 ;       cRecRes++;
        MOV     FCBErr,FTRIM + FEOF     ;       FCBErr = FTRIM | FEOF;
        MOV     CX,RecSize              ;       Blank (RecSize-cResult%RecSize,
        SUB     CX,DX                   ;              DMA+cResult);
        XOR     AL,AL

;hkn;   SS override
        LES     DI,DMAAdd
        ADD     DI,cResult
        REP     STOSB                   ;   }   }
SetExt:
        MOV     DX,RecPosH
        MOV     AX,RecPosL
        TESTB   FCBOp,RANDOM            ;   if ((OP&Random) == 0 ||
        JZ      DoSetExt
        TESTB   FCBOp,BLOCK             ;       (OP&BLOCK) <> 0)
        JZ      TrySetRR
DoSetExt:
        invoke  SetExtent               ;       SetExtent (RecPos, FCB);
TrySetRR:
        TESTB   FCBOp,BLOCK             ;   if ((op&BLOCK) <> 0)
        JZ      TryReturn
        MOV     WORD PTR [SI.FCB_RR],AX ;       FCB->RR = RecPos;
        MOV     BYTE PTR [SI.FCB_RR+2],DL
        CMP     [SI.fcb_RECSIZ],64
        JAE     TryReturn
        MOV     [SI+fcb_RR+2+1],DH      ; Set 4th byte only if record size < 64
TryReturn:
        TESTB   FCBOP,FCBRead           ;   if (!(FCBOP & FCBREAD)) {
        JNZ     FCBSave
        SAVE    <DS>                    ;       FCB->FDate = date;
        Invoke  Date16                  ;       FCB->FTime = time;
        RESTORE <DS>
        MOV     [SI].FCB_FDate,AX
        MOV     [SI].FCB_FTime,DX       ;       }
FCBSave:
        TESTB   FCBOp,BLOCK             ;   if ((op&BLOCK) <> 0)
        JZ      DoReturn
        MOV     CX,cRecRes              ;       user_CX = cRecRes;
        invoke  Get_User_Stack
        MOV     [SI.User_CX],CX
DoReturn:
        MOV     AL,FCBErr               ;   return (FCBERR);
        Leave
        return
EndProc FCBIO

Break <$FCB_Open - open an old-style FCB>
;---------------------------------------------------------------------------
;
;   $FCB_Open - CPM compatability file open.  The user has formatted an FCB
;       for us and asked to have the rest filled in.
;
;   Inputs:     DS:DX point to an unopenned FCB
;   Outputs:    AL indicates status 0 is ok FF is error
;               FCB has the following fields filled in:
;                   Time/Date Extent/NR Size
;---------------------------------------------------------------------------

Procedure $FCB_Open,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     AX,sharing_Compat+Open_For_Both

;hkn; DOS_Open is in DOSCODE
        MOV     CX,OFFSET DOSCODE:DOS_Open
;
; The following is common code for Creation and openning of FCBs.  AX is
; either attributes (for create) or open mode (for open)...  DS:DX points to
; the FCB
;
DoAccess:
        SAVE    <DS,DX,CX,AX>           ; save FCB pointer away

;hkn;   OpenBuf is in DOSDATA
        MOV     DI,OFFSET DOSDATA:OpenBuf
        invoke  TransFCB                ; crunch the fcb
        RESTORE <AX,CX,DX,DS>   ; get fcb
        JNC     FindFCB                 ; everything seems ok
FCBOpenErr:
;
; AL has error code
;
        transfer    FCB_Ret_Err
FindFCB:
        invoke  GetExtended             ; DS:SI will point to FCB

        push    ax
        mov     al,1                    ;indicate Open/Create operation
        invoke  LRUFCB                  ; get a sft entry (no error)
        pop     ax

        JC      HardMessage
        ASSUME  ES:NOTHING

        MOV     ES:[DI].sf_mode,sf_ISFCB
        SAVE    <DS,SI,BX>              ; save fcb pointer
        MOV     SI,CX

;hkn; SS is DOSDATA
        Context DS                      ; let DOS_Open see variables
        CALL    SI                      ; go open the file
        RESTORE <BX,SI,DS>              ; get fcb
        ASSUME  DS:NOTHING

;hkn; SS override
        LES     DI,ThisSFT              ; get sf pointer
        JNC     FCBOK                   ; operation succeeded
        Assert  ISSFT,<ES,DI>,"DeadFCB"
failopen:
        PUSH    AX
        MOV     AL,"R"                  ; clear out field (free sft)
        invoke  BlastSFT
        POP     AX
        CMP     AX,error_too_many_open_files
        JZ      HardMessage
        CMP     AX,error_sharing_buffer_exceeded
        jnz     DeadFCB
HardMessage:
        PUSH    AX
        invoke  FCBHardErr
        POP     AX
DeadFCB:
IFDEF __TONYI__1
;----------------------------------------------
; Save a byte......
;----------------------------------------------
	jmp	short FCBOpenErr
ELSE
        transfer    FCB_Ret_Err
ENDIF
FCBOK:
        invoke  IsSFTNet                       ;AN007;F.C. >32mb  Non Fat file?
        JNZ     FCBOK2                         ;AN007;F.C. >32mb  yes
        invoke  CheckShare                     ;AN000;F.C. >32mb  share around?
        JNZ     FCBOK2                         ;AN000;F.C. >32mb  yes
;SR;
; If we reach here, we know we have got a local SFT. Let's update the
; LocalSFT variable to reflect this.
;
        mov     word ptr LocalSFT,di    ;
        mov     word ptr LocalSFT+2,es; Store the SFT address
FCBOK2:

        INC     ES:[DI].sf_ref_count    ; increment reference count
        invoke  SaveFCBInfo
        Assert  ISSFT,<ES,DI>,"FCBOK"

        TESTB   ES:[DI].sf_flags,devid_device
        JNZ     FCBNoDrive              ; do not munge drive on devices
        MOV     AL,DS:[SI]              ; get drive byte
        invoke  GetThisDrv              ; convert
        INC     AL
        MOV     DS:[SI],AL              ; stash in good drive letter
FCBNoDrive:
        MOV     [SI].FCB_RecSiz,80h     ; stuff in default record size
IFDEF __TONYI__1
;---------------------------------------------------
; Save 6 bytes....
;---------------------------------------------------
	.errnz	(fcb_ftime - fcb_fdate - 2)
	.errnz	(sf_date - sf_time - 2)
	push	es
	les	ax, dword ptr es:[di].sf_time
	MOV	[SI].FCB_FTime,AX
	MOV	[SI].FCB_FDate,es
	pop	es
	push	es
	les	ax, dword ptr es:[di].SF_Size ; set sizes
	MOV	[SI].FCB_FILSIZ,AX
	MOV	[SI].FCB_FILSIZ+2,es
	pop	es
ELSE
        MOV     AX,ES:[DI].SF_Time      ; set time
        MOV     [SI].FCB_FTime,AX
        MOV     AX,ES:[DI].SF_Date      ; set date
        MOV     [SI].FCB_FDate,AX
        MOV     AX,WORD PTR ES:[DI].SF_Size ; set sizes
        MOV     [SI].FCB_FILSIZ,AX
        MOV     AX,WORD PTR ES:[DI].SF_Size+2
        MOV     [SI].FCB_FILSIZ+2,AX
ENDIF
        XOR     AX,AX                   ; convenient zero
        MOV     [SI].FCB_Extent,AX      ; point to beginning of file
;
; We must scan the set of FCB SFTs for one that appears to match the current
; one.  We cheat and use CheckFCB to match the FCBs.
;

;hkn;   SS override
        LES     DI,SFTFCB               ; get the pointer to head of the list
        MOV     AH,BYTE PTR ES:[DI].sfCount ; get number of SFTs to scan
OpenScan:
        CMP     AL,[SI].fcb_sfn         ; don't compare ourselves
        JZ      SkipCheck
        SAVE    <AX>                    ; preserve count
        invoke  CheckFCB                ; do they match
        RESTORE <AX>            ; get count back
        JNC     OpenFound               ; found a match!
SkipCheck:
        INC     AL                      ; advance to next FCB
        CMP     AL,AH                   ; table full?
        JNZ     OpenScan                ; no, go for more
OpenDone:
        xor     al,al                   ; return success
        return
;
; The SFT at ES:DI is the one that is already in use for this FCB.  We set the
; FCB to use this one.  We increment its ref count.  We do NOT close it at all.
; Consider:
;
;   open (foo)  delete (foo) open (bar)
;
; This causes us to recycle (potentially) bar through the same local SFT as
; foo even though foo is no longer needed; this is due to the server closing
; foo for us when we delete it.  Unfortunately, we cannot see this closure.
; If we were to CLOSE bar, the server would then close the only reference to
; bar and subsequent I/O would be lost to the redirector.
;
; This gets solved by NOT closing the sft, but zeroing the ref count
; (effectively freeing the SFT) and informing the sharer (if relevant) that
; the SFT is no longer in use.  Note that the SHARER MUST keep its ref counts
; around.  This will allow us to access the same file through multiple network
; connections and NOT prematurely terminate when the ref count on one
; connection goes to zero.
;
OpenFound:
        MOV     [SI].fcb_SFN,AL         ; assign with this
        INC     ES:[DI].sf_ref_count    ; remember this new invocation
        MOV     AX,FCBLRU               ; update LRU counts
        MOV     ES:[DI].sf_LRU,AX
;
; We have an FCB sft that is now of no use.  We release sharing info and then
; blast it to prevent other reuse.
;

;hkn; SS is DOSDATA
        context DS
        LES     DI,ThisSFT
        DEC     ES:[DI].sf_ref_count    ; free the newly allocated SFT
        invoke  ShareEnd
        Assert  ISSFT,<ES,DI>,"Open blasting"
        MOV     AL,'C'
        invoke  BlastSFT
        JMP     SHORT OpenDone                                        ;AC000;
EndProc $FCB_Open

BREAK   <$FCB_Create - create a new directory entry>
;----------------------------------------------------------------------------
;
;   $FCB_Create - CPM compatability file create.  The user has formatted an
;       FCB for us and asked to have the rest filled in.
;
;   Inputs:     DS:DX point to an unopenned FCB
;   Outputs:    AL indicates status 0 is ok FF is error
;               FCB has the following fields filled in:
;                   Time/Date Extent/NR Size
;----------------------------------------------------------------------------

Procedure $FCB_Create,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA

;hkn; DOS_Create is in DOSCODE
        MOV     CX,OFFSET DOSCODE:DOS_Create    ; routine to call
        XOR     AX,AX                   ; attributes to create
        invoke  GetExtended             ; get extended FCB
        JZ      DoAccessJ               ; not an extended FCB
        MOV     AL,[SI-1]               ; get attributes
DoAccessJ:
        JMP     DoAccess                ; do dirty work
EndProc $FCB_Create

IFDEF __TONYI__
;----------------------------------------------
; By moving these to just above FCBIO() we can 
; turn the long JMP's into SHORT JMP's, thus
; saving several bytes for free.
;----------------------------------------------
ELSE
BREAK <$FCB_Random_write_Block - write a block of records to a file >
;----------------------------------------------------------------------------
;
;   $FCB_Random_Write_Block - retrieve a location from the FCB, seek to it
;       and write a number of blocks from it.
;
;   Inputs:     DS:DX point to an FCB
;   Outputs:    AL = 0 write was successful and the FCB position is updated
;               AL <> 0 Not enough room on disk for the output
;
;----------------------------------------------------------------------------

Procedure $FCB_Random_Write_Block,NEAR

        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     AL,Random+Block
        JMP     FCBIO

EndProc $FCB_Random_Write_Block

BREAK <$FCB_Random_Read_Block - read a block of records to a file >
;----------------------------------------------------------------------------
;
;   $FCB_Random_Read_Block - retrieve a location from the FCB, seek to it
;       and read a number of blocks from it.
;
;   Inputs:     DS:DX point to an FCB
;   Outputs:    AL = error codes defined above
;
;----------------------------------------------------------------------------

Procedure $FCB_Random_Read_Block,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     AL,Random+FCBRead+Block
        JMP     FCBIO
EndProc $FCB_Random_Read_Block

BREAK <$FCB_Seq_Read - read the next record from a file >
;----------------------------------------------------------------------------
;
;   $FCB_Seq_Read - retrieve the next record from an FCB and read it into
;       memory
;
;   Inputs:     DS:DX point to an FCB
;   Outputs:    AL = error codes defined above
;
;----------------------------------------------------------------------------

Procedure $FCB_Seq_Read,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     AL,FCBRead
        JMP     FCBIO
EndProc $FCB_Seq_Read

BREAK <$FCB_Seq_Write - write the next record to a file >
;----------------------------------------------------------------------------
;
;   $FCB_Seq_Write - retrieve the next record from an FCB and write it to the
;       file
;
;   Inputs:     DS:DX point to an FCB
;   Outputs:    AL = error codes defined above
;
;----------------------------------------------------------------------------

Procedure $FCB_Seq_Write,NEAR

        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     AL,0
        jmp     FCBIO

EndProc $FCB_SEQ_WRITE

BREAK <$FCB_Random_Read - Read a single record from a file >
;----------------------------------------------------------------------------
;
;   $FCB_Random_Read - retrieve a location from the FCB, seek to it and read a
;       record from it.
;
;   Inputs:     DS:DX point to an FCB
;   Outputs:    AL = error codes defined above
;
;----------------------------------------------------------------------------

Procedure $FCB_Random_Read,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     AL,Random+FCBRead
        jmp     FCBIO                   ; single block
EndProc $FCB_RANDOM_READ

BREAK <$FCB_Random_Write - write a single record to a file >
;----------------------------------------------------------------------------
;
;   $FCB_Random_Write - retrieve a location from the FCB, seek to it and write
;       a record to it.
;
;   Inputs:     DS:DX point to an FCB
;   Outputs:    AL = error codes defined above
;
;----------------------------------------------------------------------------

Procedure $FCB_Random_Write,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     AL,Random
        jmp     FCBIO
EndProc $FCB_RANDOM_WRITE
ENDIF

DOSCODE ENDS
        END
