        TITLE   FILE - Pathname related system calls
        NAME    FILE


IF 1
__TONYI__ = 1           ; Speed/Space hacks
__TONYI__1= 1           ; Call SetErrLocUnk
__TONYI_486__= 1        ; 486 pipeline optimizations (8088 benign)
VCB_FIX_215A = 1        ; Fix for bug in INT 21/5A if full root dir
ENDIF


;
;       Microsoft Confidential
;       Copyright (C) Microsoft Corporation 1991
;       All Rights Reserved.
;
;**     FILE.ASM - Pathname Related System Calls
;
;       These will be passed direct text of the
;       pathname from the user.  They will need to be passed through the macro
;       expander prior to being sent through the low-level stuff.  I/O specs are
;       defined in DISPATCH.    The system calls are:
;
;       $Open         written
;       $Creat        written
;       $ChMod        written
;       $Unlink       written
;       $Rename       written
;       $CreateTempFile   written
;       $CreateNewFile    written
;       $Extended_Open    written  DOS 4.00
;       GetIOParms            written  DOS 4.00
;
;       Revision history:
;
;           Created: MZ 4 April 1983
;           A000   version 4.00  Jan. 1988
;           A001   DOS 4.0, PTM #6069, BL, ROMDOS fix
;           M022   conditional removed
;           M049   If access_denied is obtained whle trying to create a temp
;                  file, check to see if this was due to the network drive
;                  being read only.
;
;       M069    Bug #5913. CreateTempFile now checks for error_net_access_
;               denied instead of error_cannot_make because ExtErr is set
;               to the first errorcode when we try to create a file on a
;               read-only share.
;
;       M070    Bug #5943. CreateTempFile now has a check for
;               error_failed_I24 which is set if the user fails on an I24
;               that is got on a write-protected disk. Previously, the
;               code would go ahead and try another name and then hang
;               because DOS is in an inconsistent state.
;

        .xlist
        .xcref
        include version.inc
        include dosseg.inc
        include dossym.inc
        include devsym.inc
        include sf.inc
        include filemode.inc
        include bugtyp.inc
        include curdir.inc
        .cref
        .list

        I_need  WFP_Start,WORD          ; pointer to beginning of expansion
        I_Need  ThisCDS,DWORD           ; pointer to curdir in use
        I_need  ThisSft,DWORD           ; SFT pointer for DOS_Open
        I_need  pJFN,DWORD              ; temporary spot for pointer to JFN
        I_need  JFN,WORD                ; word JFN for process
        I_need  SFN,WORD                ; word SFN for process
        I_Need  OpenBuf,128             ; buffer for filename
        I_Need  RenBuf,128              ; buffer for filename in rename
        I_need  Sattrib,BYTE            ; byte attribute to search for
        I_need  Ren_WFP,WORD            ; pointer to real path
        I_need  cMeta,BYTE
        I_need  EXTERR,WORD             ; extended error code
        I_need  EXTERR_LOCUS,BYTE       ; Extended Error Locus
        i_need  JShare,DWORD            ; share jump table
        I_need  fSharing,BYTE           ; TRUE => via ServerDOSCall
        I_need  FastOpenTable,BYTE
        I_need  CPSWFLAG,BYTE           ;AN000;FT. cpsw falg
        I_need  EXTOPEN_FLAG,WORD       ;AN000;FT. extended file open flag
        I_need  EXTOPEN_ON,BYTE         ;AN000;FT. extended open flag
        I_need  EXTOPEN_IO_MODE,WORD    ;AN000;FT. IO mode
        I_need  SAVE_ES,WORD            ;AN000;;FT. for get/set XA
        I_need  SAVE_DI,WORD            ;AN000;;FT. for get/set XA
        I_need  SAVE_DS,WORD            ;AN000;;FT. for get/set XA
        I_need  SAVE_SI,WORD            ;AN000;;FT. for get/set XA
        I_need  SAVE_DX,WORD            ;AN000;;FT. for get/set XA
        I_need  SAVE_BX,WORD            ;AN000;;FT. for get/set XA
        I_need  SAVE_CX,WORD            ;AN000;;FT. for get/set XA
        I_need  Temp_Var,WORD           ;AN000;;
        I_need  DOS34_FLAG,WORD         ;AN000;;
        I_need  Temp_Var2,WORD          ;AN000;;

DOSCODE SEGMENT
        ASSUME  SS:DOSDATA,CS:DOSCODE
        EXTRN   DOS_OPEN:NEAR,DOS_CREATE:NEAR,DOS_Create_New:NEAR

        EXTRN   SFNFree:near
        extrn   Set_EXT_mode:near               ;M022 conditional removed

        BREAK <$Open - open a file from a path string>


;**     $OPen - Open a File
;
;       given a path name in DS:DX and an open mode in AL, $Open opens the
;       file and and returns a handle
;
;       ENTRY   (DS:DX) = pointer to asciz name
;               (AL) = open mode
;       EXIT    'C' clear if OK
;                 (ax) = file handle
;               'C' set iff error
;                 (ax) = error code
;       USES    all

Procedure   $Open,NEAR
        DOSAssume   <SS>,"Open"

        fmt TypSysCall,LevArgs,<" Mode = $x file = '$S'\n">,<AX,DS,DX>
        XOR     AH,AH
Entry $Open2                            ;AN000;
        mov     ch,attr_hidden+attr_system+attr_directory
        call    SetAttrib
        MOV     CX,OFFSET DOSCODE:DOS_Open ; address of routine to call
        SAVE    <AX>                    ; Save mode on stack
IFDEF DBCS                              ;AN000;
        MOV     Temp_Var,0              ;AN000;KK. set variable with 0;smr;SS Override
ENDIF                                   ;AN000;


;*      General file open/create code.  The $CREATE call and the various
;       $OPEN calls all come here.
;
;       We'll share a lot of the standard stuff of allocating SFTs, cracking
;       path names, etc., and then dispatch to our individual handlers.
;       WARNING - this info and list is just a guess, not definitive - jgl
;
;       (TOS) = create mode
;       (CX) = address of routine to call to do actual function
;       (DS:DX) = ASCIZ name
;       SAttrib = Attribute mask

;       Get a free SFT and mark it "being allocated"

AccessFile:
IFDEF  DBCS                             ;AN000;
        TEST    Temp_Var,ATTR_VOLUME_ID ; volume id bit set?
        JZ      novol
        OR      [DOS34_FLAG],DBCS_VOLID ; warn transpath about VOLID
novol:
ENDIF
        EnterCrit   critSFT
        call    SFNFree                 ; get a free sfn
        LeaveCrit   critSFT
        JC      OpenFailJ               ; oops, no free sft's
        MOV     SFN,BX                  ; save the SFN for later;smr;SS Override
        fmt     TypAccess,LevSFN,<"AccessFile setting SFN to $x\n">,<BX>
        MOV     WORD PTR ThisSFT,DI     ; save the SF offset    ;smr;SS Override
        MOV     WORD PTR ThisSFT+2,ES   ; save the SF segment   ;smr;SS Override

;       Find a free area in the user's JFN table.

        invoke  JFNFree                 ; get a free jfn
IFDEF __TONYI__
;--------------------------------------------------------
; Streamline the non-FAIL case.......
;--------------------------------------------------------
OpenFailJ:
        jc      OpenFail ; CF is still set if we get here from the JC above
ELSE
        JNC     SaveJFN
OpenFailJ:
        JMP     OpenFail                ; there were free JFNs... try SFN
ENDIF

SaveJFN:MOV     WORD PTR pJFN,DI        ; save the jfn offset   ;smr;SS Override
        MOV     WORD PTR pJFN+2,ES      ; save the jfn segment  ;smr;SS Override
        MOV     JFN,BX                  ; save the jfn itself   ;smr;SS Override

;       We have been given an JFN.  We lock it down to prevent other tasks from
;       reusing the same JFN.
IFDEF __TONYI_486__
        MOV     BX,SFN                                          ;smr;SS Override
        MOV     SI,DX                   ; get name in appropriate place
        MOV     ES:[DI],BL              ; assign the JFN
ELSE
        MOV     BX,SFN                                          ;smr;SS Override
        MOV     ES:[DI],BL              ; assign the JFN
        MOV     SI,DX                   ; get name in appropriate place
ENDIF
        MOV     DI,OFFSET DOSDATA:OpenBuf  ; appropriate buffer
        SAVE    <CX>                    ; save routine to call
        invoke  TransPath               ; convert the path
        RESTORE <BX>                    ; (bx) = routine to call
        LDS     SI,ThisSFT                                      ;smr;SS Override
    ASSUME DS:NOTHING
IFDEF __TONYI__
;-------------------------------------------------
; Streamline...'OpenClean' is within range...
;-------------------------------------------------
        JC      OpenClean               ; no error, go and open file
ELSE
        JC      OpenCleanJ              ; no error, go and open file
ENDIF
        CMP     cMeta,-1                                        ;smr;SS Override
        JZ      SetSearch
        MOV     AL,error_file_not_found ; no meta chars allowed
OpenCleanJ:
        JMP     short OpenClean

IFDEF __TONYI__
;-------------------------------------------------
; Move OpenFail up here to get it within range...
; This makes a normal case above work better.
;-------------------------------------------------
OpenFail:
        STI
        RESTORE <CX>            ; Clean stack
        jmp     short OpenCritLeave
ENDIF

SetSearch:
        RESTORE <AX>            ; Mode (Open), Attributes (Create)

;       We need to get the new inheritance bits.

        xor     cx,cx
IFDEF __TONYI_486__
        CMP     BX,OFFSET DOSCODE:DOS_OPEN
        MOV     [SI].sf_mode,cx         ; initialize mode field to 0
        MOV     [SI].SF_mft,cx          ; clean out sharing info
ELSE
        MOV     [SI].sf_mode,cx         ; initialize mode field to 0
        MOV     [SI].SF_mft,cx          ; clean out sharing info
        CMP     BX,OFFSET DOSCODE:DOS_OPEN
ENDIF
        JNZ     DoOper
        TEST    AL,sharing_no_inherit   ; look for no inher
        JZ      DoOper
        AND     AL,07Fh                 ; mask off inherit bit
        MOV     CX,sf_no_inherit
DoOper:

;**     Check if this is an extended open. If so you must set the
;       modes in sf_mode. Call Set_EXT_mode to do all this. See
;       Set_EXT_mode in creat.asm

        SAVE    <di, es>                ;M022 conditional removed here
        push    ds
        pop     es
        push    si
        pop     di              ; (es:di) = SFT address
        call    Set_EXT_mode
        RESTORE <es, di>

        Context DS
        SAVE    <CX>
        CALL    BX                      ; blam!
        RESTORE <CX>
        LDS     SI,ThisSFT
        ASSUME  DS:NOTHING
        JC      OpenE2                  ;AN000;FT. chek extended open hooks first

;       The SFT was successfully opened.  Remove busy mark.

OpenOK:
        ASSUME  DS:NOTHING
        MOV     [SI].sf_ref_count,1
        OR      [SI].sf_flags,CX        ; set no inherit bit if necessary

; If the open mode is 70, we scan the system for other SFT's with the same
; contents.  If we find one, then we can 'collapse' thissft onto the already
; opened one.  Otherwise we use this new one.  We compare uid/pid/mode/mft
;
; Since this is only relevant on sharer systems, we stick this code into the
; sharer.

        MOV     AX,JFN                                          ;smr;SS Override
if installed
        Call    JShare + 12 * 4                                 ;smr;SS Override
else
        Call    ShCol                                           ;PBUGBUG
endif
        fmt     TypAccess,LevSFN,<"AccessFile setting SFN to -1\n">
        MOV     SFN,-1                  ; clear out sfn pointer ;smr;SS Override
        transfer    Sys_Ret_OK          ; bye with no errors
;Extended Open hooks check
OpenE2:                                    ;AN000;;EO.
        CMP     AX,error_invalid_parameter ;AN000;;EO. IFS extended open ?
        JNZ     OpenE                      ;AN000;;EO. no.
        JMP     short OpenCritLeave                ;AN000;;EO. keep handle

;       Extended Open hooks check
;
;       AL has error code.  Stack has argument to dos_open/dos_create.

OpenClean:
        fmt TypSysCall,LevErr,<"Return value from transpath $x\n">,<AX>
        RESTORE <bx>            ; clean off stack
OpenE:
        MOV     [SI.SF_Ref_Count],0     ; release SFT
        LDS     SI,pJFN                                         ;smr;SS Override
        MOV     BYTE PTR [SI],0FFh      ; free the SFN...
IFDEF __TONYI__
;--------------------------------------------------
; Moved above so JMP sizings work better for
; non-error case above.
;--------------------------------------------------
ELSE
        JMP     SHORT OpenCritLeave
OpenFail:
        STI
        RESTORE <CX>            ; Clean stack
ENDIF

OpenCritLeave:
        MOV     SFN,-1                  ; remove mark.
        fmt TypSysCall,LevErr,<"Open/CreateXX: error $x\n">,<AX>
;; File Tagging DOS 4.00
        CMP     [EXTERR],error_Code_Page_Mismatched     ;AN000;;FT. code page mismatch;smr;SS Override
        JNZ     NORERR                                  ;AN000;;FT. no
        transfer From_GetSet                            ;AN000;;FT. yes
NORERR:                                                 ;AN000;

;; File Tagging DOS 4.00
        transfer    Sys_Ret_Err         ; no free, return error

EndProc $Open, NoCheck


        BREAK <$Creat - create a brand-new file>


;**     $Creat - Create a File
;
;       $Creat creates the directory entry specified in DS:DX and gives it the
;       initial attributes contained in CX
;
;       ENTRY   (DS:DX) = ASCIZ path name
;               (CX) = initial attributes
;       EXIT    'C' set if error
;                 (ax) = error code
;               'C' clear if OK
;                 (ax) = file handle
;       USES    all

Procedure   $Creat,NEAR
        DOSAssume   <SS>,"Creat"

        fmt TypSysCall,LevArgs,<"file = '$S'\n">,<DS,DX>
IFDEF DBCS                              ;AN000;
        MOV     Temp_Var,CX             ;AN000;KK. set variable with attribute        ;AN000;smr;SS Override
ENDIF                                   ;AN000;
        SAVE    <CX>                    ; Save attributes on stack
        MOV     CX,OFFSET DOSCODE:DOS_Create; routine to call
AccessSet:
        mov     SAttrib,attr_hidden+attr_system                 ;smr;SS Override
        JMP     AccessFile              ; use good ol' open

EndProc $Creat, NoCheck


        BREAK <$CHMOD - change file attributes>

;**     $CHMOD - Change File Attributes
;
;   Assembler usage:
;           LDS     DX, name
;           MOV     CX, attributes
;           MOV     AL,func (0=get, 1=set)
;           INT     21h
;   Error returns:
;           AX = error_path_not_found
;           AX = error_access_denied
;

procedure $CHMOD,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        MOV     DI,OFFSET DOSDATA:OpenBuf  ; appropriate buffer
        SAVE    <AX,CX>                 ; save function and attributes
        MOV     SI,DX                   ; get things in appropriate places
        invoke  TransPathSet            ; get correct path
        RESTORE <CX,AX>         ; and get function and attrs back
        JC      ChModErr                ; errors get mapped to path not found
        Context DS                      ; set up for later possible calls
        CMP     cMeta,-1
        JNZ     ChModErr
        MOV     [SAttrib],attr_hidden+attr_system+attr_directory
        SUB     AL,1                    ; fast way to discriminate
        JB      ChModGet                ; 0 -> go get value
        JZ      ChModSet                ; 1 -> go set value
IFDEF __TONYI__1
;---------------------------------------------------
; Save 3 bytes......
;---------------------------------------------------
        invoke  SetErrLocUnk
ELSE
        MOV     EXTERR_LOCUS,errLoc_Unk ; Extended Error Locus
ENDIF
        error   error_invalid_function  ; bad value
ChModGet:
        invoke  Get_File_Info           ; suck out the ol' info
        JC      ChModE                  ; error codes are already set for ret
        invoke  Get_User_stack          ; point to user saved vaiables
        assume  DS:NOTHING              ;smr;PBUGBUG
        MOV     [SI.User_CX],AX         ; return the attributes
        transfer    Sys_Ret_OK          ; say sayonara
ChModSet:
        MOV     AX,CX                   ; get attrs in position
        invoke  Set_File_Attribute      ; go set
        JC      ChModE                  ; errors are set
IFDEF __TONYI__
;---------------------------------------------------
; Save a byte with a short JMP
;---------------------------------------------------
        jmp     short TailMerge_Sys_Ret_OK
ELSE
        transfer    Sys_Ret_OK
ENDIF
ChModErr:
        mov     al,error_path_not_found
ChmodE:
        Transfer    SYS_RET_ERR
EndProc $ChMod

        BREAK <$UNLINK - delete a file entry>

;**     $UNLINK - Delete a File
;
;
;       Assembler usage:
;           LDS     DX, name
;           IF VIA SERVER DOS CALL
;            MOV     CX,SEARCH_ATTRIB
;           MOV     AH, Unlink
;           INT     21h
;
;       ENTRY   (ds:dx) = path name
;               (cx) = search_attribute, if via server_dos
;       EXIT    'C' clear if no error
;               'C' set if error
;                 (ax) = error code
;                       = error_file_not_found
;                       = error_access_denied

procedure $UNLINK,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA

        SAVE    <CX>                    ; Save possible CX input parm
        MOV     SI,DX                   ; Point at input string
        MOV     DI,OFFSET DOSDATA:OpenBuf  ; temp spot for path
        invoke  TransPathSet            ; go get normalized path
        RESTORE <CX>
        JC      ChModErr                ; badly formed path
        CMP     cMeta,-1                ; meta chars?           ;smr;SS Override
IFDEF __TONYI__
        jnz     ChModErr        ; Tail merged to code above.........
ELSE
        JNZ     NotFound
ENDIF
        Context DS
        mov     ch,attr_hidden+attr_system   ; unlink appropriate files
        call    SetAttrib
        invoke  DOS_Delete              ; remove that file
IFDEF __TONYI__
TailMerge_SYS_RET_ERR_if_carry:
        jc      ChmodE          ; Tail merged to code above.........
TailMerge_Sys_Ret_OK:
ELSE
        JC      UnlinkE                 ; error is there
ENDIF
        transfer    Sys_Ret_OK          ; okey doksy

IFDEF __TONYI__
;-------------------------------------------------------------
; We'll tail merge these operations with identical things in
; $ChMod().  This'll save 5 bytes.
;-------------------------------------------------------------
ELSE
NotFound:
        MOV     AL,error_path_not_found
UnlinkE:
        transfer    Sys_Ret_Err         ; bye
ENDIF
EndProc $UnLink

BREAK <$RENAME - move directory entries around>
;
;   Assembler usage:
;           LDS     DX, source
;           LES     DI, dest
;           IF VIA SERVER DOS CALL
;            MOV     CX,SEARCH_ATTRIB
;           MOV     AH, Rename
;           INT     21h
;
;   Error returns:
;           AX = error_file_not_found
;              = error_not_same_device
;              = error_access_denied

procedure $RENAME,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        SAVE    <CX,DS,DX>              ; save source and possible CX arg
        PUSH    ES
        POP     DS                      ; move dest to source
        MOV     SI,DI                   ; save for offsets
        MOV     DI,OFFSET DOSDATA:RenBuf


        invoke  TransPathSet            ; munge the paths
        PUSH    WFP_Start               ; get pointer           ;smr;SS Override
        POP     Ren_WFP                 ; stash it              ;smr;SS Override
        RESTORE <SI,DS,CX>              ; get back source and possible CX arg
epjc2:  JC      ChModErr                ; get old error
        CMP     cMeta,-1                                        ;smr;SS Override
IFDEF __TONYI__
        jnz     ChModErr        ; Merged above........
ELSE
        JNZ     NotFound
ENDIF
        SAVE    <CX>                    ; Save possible CX arg
        MOV     DI,OFFSET DOSDATA:OpenBuf  ; appropriate buffer
        invoke  TransPathSet            ; wham
        RESTORE <CX>
        JC      EPJC2
        Context DS
        CMP     cMeta,-1
IFDEF __TONYI__
        jb      ChModErr        ; Merged above....
ELSE
        JB      NotFound
ENDIF

IFDEF __TONYI__
;--------------------------------------------------------------------
; Since ES:DI are reloaded in the next 3 instructions we can use
; a LES DI, DWORD PTR [THISCDS] here to save some space(faster too).
; This saves 3 bytes.  Those 2 memory pushes were 5 bytes each...
; The LES is 5 and the two reg pushes 1 each...
;--------------------------------------------------------------------
        les     di, dword ptr [THISCDS]
        push    di
        push    es
ELSE
        PUSH    WORD PTR [THISCDS]         ;AN000;;MS.save thiscds
        PUSH    WORD PTR [THISCDS+2]       ;AN000;;MS.
ENDIF
        MOV     DI,OFFSET DOSDATA:OpenBuf ;AN000;;MS.
        PUSH    SS                         ;AN000;;MS.
        POP     ES                         ;AN000;;MS.es:di-> source
        XOR     AL,AL                      ;AN000;;MS.scan all CDS
rnloop:                                    ;AN000;
        invoke  GetCDSFromDrv              ;AN000;;MS.
        JC      dorn                       ;AN000;;MS.  end of CDS
        invoke  StrCmp                     ;AN000;;MS.  current dir ?
        JZ      rnerr                      ;AN000;;MS.  yes
        INC     AL                         ;AN000;;MS.  next
        JMP     SHORT rnloop               ;AC000;;MS.
rnerr:                                     ;AN000;
IFDEF __TONYI__
;----------------------------------------------------------------------
; "error" macro whacks AL, and since this is an error path, speed isn't
; real critical, so we'll save some space.
;----------------------------------------------------------------------
        pop     ax
        pop     ax
ELSE
        ADD     SP,4                       ;AN000;;MS. pop thiscds
ENDIF
        error   error_current_directory    ;AN000;;MS.
dorn:                                      ;AN000;
        POP     WORD PTR SS:[THISCDS+2]    ;AN000;;MS.;PBUGBUG;SS REQD??
        POP     WORD PTR SS:[THISCDS]      ;AN000;;MS.;PBUGBUG;SS REQD??
        Context DS
        mov     ch,attr_directory+attr_hidden+attr_system; rename appropriate files
        call    SetAttrib
        invoke  DOS_Rename              ; do the deed
IFDEF __TONYI__
        jc      TailMerge_SYS_RET_ERR_if_carry  ; Merged above......
ELSE
        JC      UnlinkE                 ; errors
ENDIF


IFDEF __TONYI__
;---------------------------------------------------
; Save a byte with a short JMP
;---------------------------------------------------
        jmp     short TailMerge_Sys_Ret_OK
ELSE
        transfer    Sys_Ret_OK
ENDIF
EndProc $Rename

Break <$CreateNewFile - Create a new directory entry>

;
;   CreateNew - Create a new directory entry.  Return a file handle if there
;       was no previous directory entry, and fail if a directory entry with
;       the same name existed previously.
;
;   Inputs:     DS:DX point to an ASCIZ file name
;               CX contains default file attributes
;   Outputs:    Carry Clear:
;                   AX has file handle opened for read/write
;               Carry Set:
;                   AX has error code
;   Registers modified: All

Procedure $CreateNewFile,NEAR
        ASSUME  CS:DOSCODE,SS:DOSDATA
        fmt TypSysCall,LevArgs,<" Att = $x file = '$S'\n">,<CX,DS,DX>
IFDEF DBCS                              ;AN000;
        MOV     Temp_Var,CX             ;AN000;KK. set variable with attribute;smr;SS Override
ENDIF                                   ;AN000;
        SAVE    <CX>                    ; Save attributes on stack
        MOV     CX,OFFSET DOSCODE:DOS_Create_New   ; routine to call
        JMP     AccessSet               ; use good ol' open

EndProc $CreateNewFile, NoCheck

        Break   <BinToAscii - convert a number to a string>

;**     BinToAscii - conver a number to a string.
;
;       BinToAscii converts a 16 bit number into a 4 ascii characters.
;       This routine is used to generate temp file names so we don't spend
;       the time and code needed for a true hex number, we just use
;       A thorugh O.
;
;       ENTRY   (ax) = value
;               (es:di) = destination
;       EXIT    (es:di) updated by 4
;       USES    cx, di, flags

Procedure   BinToAscii,NEAR
IFDEF __TONYI__
;---------------------------------------------------------------------
; This code is 40% faster.  The letters come out a little different...
; for example 1234H used to become "BCDE", it now becomes "CBED".
; Who cares, right?  The name is still unique.
; Note: This new stuff is identical in size to the old stuff...
;---------------------------------------------------------------------
; For those purists who'd like identical names as before...uncomment
; the XCHG AL,AH's and you'll still be 33% faster, but a tad bigger.
;---------------------------------------------------------------------
; This uses a coding trick with the AAM instruction.  Normally AAM
; has an implied base of 10(decimal) built into the instruction.  In
; fact, this can be changed to anything you want.  For example when
; it's changed to 16(decimal) nice things happen.  AAM can be used to
; cleanly split the nibbles in AL into AH and AL.  For example:
;
;   mov  ax, 1234H
;   mutantAAM
; *** At this point AH will be 03H and AL will be 04H **
;
; Neat eh?
;
; This trick has been verified to work on all the Intel CPU and the
; NEC V20 chips.  It's also used in Borland's C runtime, OS/2's
; trap register dump code etc... so there's no chance a chip vendor
; will break this behavior in the future.  It's here to stay.
;---------------------------------------------------------------------
        push    ax
        xchg    ah,al
        db      0D4H,10H        ; Split nibbles with mutant AAM
        add     ax, 4141H       ; Convert to ASCII (+ 'AA')
;;;     xchg    al, ah          ; Purists, can uncomment this...
        stosw
        pop     ax
        db      0D4H,10H        ; Split nibbles with mutant AAM
        add     ax, 4141H       ; Convert to ASCII (+ 'AA')
;;;     xchg    al, ah          ; Purists, can uncomment this...
        stosw
;---------------------------------------------------------------------
ELSE
        mov     cx,404h                 ; (ch) = digit counter, (cl) = shift cnt
bta5:   ROL     AX,CL                   ; move leftmost nibble into rightmost
        SAVE    <AX>                    ; preserve remainder of digits
        AND     AL,0Fh                  ; grab low nibble
        ADD     AL,'A'                  ; turn into ascii
        STOSB                           ; drop in the character
        RESTORE <AX>                    ; (ax) = shifted number
        dec     ch
        jnz     bta5                    ; process 4 digits
ENDIF
        return

EndProc BinToAscii

Break   <$CreateTempFile - create a unique name>

;
;   $CreateTemp - given a directory, create a unique name in that directory.
;       Method used is to get the current time, convert to a name and attempt
;       a create new.  Repeat until create new succeeds.
;
;   Inputs:     DS:DX point to a null terminated directory name.
;               CX  contains default attributes
;   Outputs:    Unique name is appended to DS:DX directory.
;               AX has handle
;   Registers modified: all

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

        fmt TypSysCall,LevArgs,<" Att = $x dir = '$S'\n">,<CX,DS,DX>
PUBLIC FILE001S,FILE001E
FILE001S:
        LocalVar    EndPtr,DWORD
        LocalVar    FilPtr,DWORD
        LocalVar    Attr,WORD
FILE001E:
        Enter
        TEST    CX,NOT attr_changeable
        JZ      OKatts                  ; Ok if no non-changeable bits set
;
; We need this "hook" here to detect these cases (like user sets one both of
; vol_id and dir bits) because of the structure of the or $CreateNewFile loop
; below.  The code loops on error_access_denied, but if one of the non
; changeable attributes is specified, the loop COULD be infinite or WILL be
; infinite because CreateNewFile will fail with access_denied always.  Thus we
; need to detect these cases before getting to the loop.
;
        MOV     AX,error_access_denied
        JMP     SHORT SETTMPERR

OKatts:
        MOV     attr,CX                 ; save attribute
        MOV     FilPtrL,DX              ; pointer to file
        MOV     FilPtrH,DS
        MOV     EndPtrH,DS              ; seg pointer to end of dir
        PUSH    DS
        POP     ES                      ; destination for nul search
        MOV     DI,DX
        MOV     CX,DI
        NEG     CX                      ; number of bytes remaining in segment
 IFDEF  DBCS                            ;AN000;
Kloop:                                  ;AN000;; 2/13/KK
        MOV     AL, BYTE PTR ES:[DI]    ;AN000;; 2/13/KK
        INC     DI                      ;AN000;; 2/13/KK
        OR      AL,AL                   ;AN000;; 2/13/KK
        JZ      GOTEND                  ;AN000;; 2/13/KK
        invoke  testkanj                ;AN000;; 2/13/KK
        jz      Kloop                   ;AN000;; 2/13/KK
        inc     di                      ;AN000;; Skip over second kanji byte 2/13/KK
        CMP     BYTE PTR ES:[DI],0      ;AN000;; 2/13/KK
        JZ      STOREPTH                ;AN000; When char before NUL is sec Kanji byte
                                        ;AN000; do not look for path char. 2/13/KK
        jmp     SHORT Kloop             ;AC000; 2/13/KK
GOTEND:                                 ;AN000; 2/13/KK
 ELSE                                   ;AN000;
        OR      CX,CX                   ;AN000;MS.  cx=0 ? ds:dx on segment boundary
        JNZ     okok                    ;AN000;MS.  no
IFDEF __TONYI__
;------------------------------------------------------------------
; Since we *KNOW* CX==0 if we get here we can save 2 bytes....
;------------------------------------------------------------------
        dec     cx      ; CX == -1
ELSE
        MOV     CX,-1                   ;AN000;MS.
ENDIF
okok:                                   ;AN000;
        XOR     AX,AX                   ;AN000;
        REPNZ   SCASB                   ;AN000;
 ENDIF                                  ;AN000;
        DEC     DI                      ; point back to the null
        MOV     AL,ES:[DI-1]            ; Get char before the NUL
        invoke  PathChrCmp              ; Is it a path separator?
        JZ      SETENDPTR               ; Yes
STOREPTH:
        MOV     AL,'\'
        STOSB                           ; Add a path separator (and INC DI)
SETENDPTR:
        MOV     EndPtrL,DI              ; pointer to the tail
CreateLoop:
        Context DS                      ; let ReadTime see variables
        SAVE    <BP>
        invoke  ReadTime                ; go get time
        RESTORE <BP>
;
; Time is in CX:DX.  Go drop it into the string.
;
        les     di,EndPtr               ; point to the string
        mov     ax,cx
        call    BinToAscii              ; store upper word
        mov     ax,dx
        call    BinToAscii              ; store lower word
        xor     al,al
        STOSB                           ; nul terminate
        LDS     DX,FilPtr               ; get name
ASSUME  DS:NOTHING
        MOV     CX,Attr                 ; get attr
        SAVE    <BP>
        CALL    $CreateNewFile          ; try to create a new file
        RESTORE <BP>
        JNC     CreateDone              ; failed, go try again
;
; The operation failed and the error has been mapped in AX.  Grab the extended
; error and figure out what to do.
;
                                        ; M049 - start
        CMP     AL,error_file_exists    ; Q: did file already exist
        JZ      CreateLoop              ; Y: try again
        CMP     AL,error_access_denied  ; Q: was it access denied
        JNZ     SETTMPERR               ; N: Error out
                                        ; Y: Check to see if we got this due
                                        ;    to the network drive. Note that
                                        ;    the redir will set the exterr
                                        ;    to error_cannot_make if this is
                                        ;    so.
IFDEF VCB_FIX_215A
; Fix bug which causes infinite loop when trying to create a file
; in a full root directory

        mov     al,byte ptr [ExtErr]       ; Get extended error code
        cmp     al,error_net_access_denied ; Network error ?
        je      SetTmpErr                  ; Yes
        cmp     al,error_FAIL_I24
        je      SetTmpErr

        cmp     al,error_file_exists    ; File already exists ?
        je      CreateLoop              ; Yes, retry
        les     di,[THISCDS]            ; Get current CDS
        cmp     di,-1                   ; Redirected ?
        je      CreateLoop              ; Yes
        test    es:[di.curdir_flags],curdir_isnet
        jnz     CreateLoop

ELSE    ; VCB_FIX_215A

        CMP     [EXTERR],error_net_access_denied ; M069
                                        ; See if it's REALLY an att mismatch
        je      SetTmpErr               ; no, network error, stop
;M070
; If the user failed on an I24, we do not want to try again
;
        cmp     ExtErr,error_FAIL_I24           ;User failed on I24? ;M070
        je      SetTmpErr               ;yes, do not try again ;M070

        jmp     short CreateLoop                ;attr mismatch, try again ;M070

ENDIF   ; VCB_FIX_215A
                                        ; M049 - end

SETTMPERR:
        STC
CreateDone:
        Leave
        JC      CreateFail
        transfer    Sys_Ret_OK          ; success!
CreateFail:
        transfer    Sys_Ret_Err
EndProc $CreateTempFile

Break   <SetAttrib - set the search attrib>

;
;   SetAttrib will set the search attribute (SAttrib) either to the normal
;   (CH) or to the value in CL if the current system call is through
;   serverdoscall.
;
;   Inputs:     fSharing == FALSE => set sattrib to CH
;               fSharing == TRUE => set sattrib to CL
;   Outputs:    none
;   Registers changed:  CX

procedure   SetAttrib,NEAR
        assume  SS:DOSDATA                      ;smr;
        test    fSharing,-1                                     ;smr;SS Override
        jnz     Set
        mov     cl,ch
Set:
        mov     SAttrib,cl                                      ;smr;SS Override
        return
EndProc SetAttrib


Break   <Extended_Open- Extended open the file>

; Input: AL= 0 reserved  AH=6CH
;        BX= mode
;        CL= create attribute  CH=search attribute (from server)
;        DX= flag
;        DS:SI = file name
;        ES:DI = parm list
;                          DD  SET EA list (-1) null
;                          DW  n  parameters
;                          DB  type (TTTTTTLL)
;                          DW  IOMODE
; Function: Extended Open
; Output: carry clear
;                    AX= handle
;                    CX=1 file opened
;                       2 file created/opened
;                       3 file replaced/opened
;         carry set: AX has error code
;


procedure   $Extended_Open,NEAR                                ;AN000;
        ASSUME  CS:DOSCODE,SS:DOSDATA   ;AN000;

        MOV     [EXTOPEN_FLAG],DX         ;AN000;EO. save ext. open flag;smr;SS Override
        MOV     [EXTOPEN_IO_MODE],0       ;AN000;EO. initialize IO mode;smr;SS Override
        TEST    DX,reserved_bits_mask     ;AN000;EO. reserved bits 0  ?
        JNZ     ext_inval2                ;AN000;EO. no
        MOV     AH,DL                     ;AN000;EO. make sure flag is right
        CMP     DL,0                      ;AN000;EO. all fail ?
        JZ      ext_inval2                ;AN000;EO. yes, error
        AND     DL,exists_mask            ;AN000;EO. get exists action byte
        CMP     DL,2                      ;AN000;EO, > 02
        JA      ext_inval2                ;AN000;EO. yes ,error
        AND     AH,not_exists_mask        ;AN000;EO. get no exists action byte
        CMP     AH,10H                    ;AN000;EO. > 10
        JA      ext_inval2                ;AN000;EO. yes error

        MOV     [SAVE_ES],ES              ;AN000;EO. save API parms;smr;SS Override
        MOV     [SAVE_DI],DI              ;AN000;EO.;smr;SS Override
        PUSH    [EXTOPEN_FLAG]            ;AN000;EO.;smr;SS Override
        POP     [SAVE_DX]                 ;AN000;EO.;smr;SS Override
        MOV     [SAVE_CX],CX              ;AN000;EO.;smr;SS Override
        MOV     [SAVE_BX],BX              ;AN000;EO.;smr;SS Override
        MOV     [SAVE_DS],DS              ;AN000;EO.;smr;SS Override
        MOV     [SAVE_SI],SI              ;AN000;EO.;smr;SS Override
        MOV     DX,SI                     ;AN000;EO. ds:dx points to file name
        MOV     AX,BX                     ;AN000;EO. ax= mode

        JMP     SHORT goopen2                    ;AN000;;EO.  do nromal
ext_inval2:                                      ;AN000;;EO.
        error   error_Invalid_Function           ;AN000;EO..  invalid function
ext_inval_parm:                                  ;AN000;EO..
        POP     CX                               ;AN000;EO..  pop up satck
        POP     SI                               ;AN000;EO..
        error   error_Invalid_data               ;AN000;EO..  invalid parms
IFDEF __TONYI__
;-------------------------------------------------------
; Merge label to a RET below...saves a byte.
;-------------------------------------------------------
ELSE
error_return:                                    ;AN000;EO.
        ret                                      ;AN000;EO..  return with error
ENDIF
goopen2:                                         ;AN000;
        TEST    BX,int_24_error                  ;AN000;EO..  disable INT 24 error ?
        JZ      goopen                           ;AN000;EO..  no
        OR      [EXTOPEN_ON],EXT_OPEN_I24_OFF    ;AN000;EO..  set bit to disable;smr;SS Override

goopen:                                          ;AN000;
        OR      [EXTOPEN_ON],EXT_OPEN_ON         ;AN000;EO..  set Extended Open active;smr;SS Override
        AND     [EXTOPEN_FLAG],0FFH              ;AN000;EO.create new ?;smr;SS Override
        CMP     [EXTOPEN_FLAG],ext_exists_fail + ext_nexists_create ;AN000;FT.;smr;SS Override
        JNZ     chknext                          ;AN000;;EO.  no
        invoke  $CreateNewFile                   ;AN000;;EO.  yes
        JC      error_return                     ;AN000;;EO.  error
        CMP     [EXTOPEN_ON],0                   ;AN000;;EO.  IFS does it;smr;SS Override
        JZ      ok_return2                       ;AN000;;EO.  yes
        MOV     [EXTOPEN_FLAG],action_created_opened ;AN000;EO. creted/opened;smr;SS Override
        JMP     setXAttr                         ;AN000;;EO.  set XAs
ok_return2:
        transfer SYS_RET_OK                      ;AN000;;EO.
chknext:
        TEST    [EXTOPEN_FLAG],ext_exists_open   ;AN000;;EO.  exists open;smr;SS Override
        JNZ     exist_open                       ;AN000;;EO.  yes
        invoke  $Creat                           ;AN000;;EO.  must be replace open
        JC      error_return                     ;AN000;;EO.  return with error
        CMP     [EXTOPEN_ON],0                   ;AN000;;EO.  IFS does it;smr;SS Override
        JZ      ok_return2                       ;AN000;;EO.  yes
        MOV     [EXTOPEN_FLAG],action_created_opened  ;AN000;EO. prsume create/open;smr;SS Override
        TEST    [EXTOPEN_ON],ext_file_not_exists      ;AN000;;EO. file not exists ?;smr;SS Override
        JNZ     setXAttr                              ;AN000;;EO. no
        MOV     [EXTOPEN_FLAG],action_replaced_opened ;AN000;;EO. replaced/opened;smr;SS Override
        JMP     SHORT setXAttr                        ;AN000;;EO. set XAs
error_return2:
        STC                         ; Set Carry again to flag error     ;AN001;
IFDEF __TONYI__
;------------------------------------------------
; Merged label saves a byte........
;------------------------------------------------
error_return:
ENDIF
        ret                                      ;AN000;;EO.  return with error
                                                 ;AN000;
exist_open:                                      ;AN000;
        test    fSharing,-1                      ;AN000;;EO. server doscall?;smr;SS Override
        jz      noserver                         ;AN000;;EO. no
        MOV     CL,CH                            ;AN000;;EO. cl=search attribute

noserver:
        call    $Open2                           ;AN000;;EO.  do open
        JNC     ext_ok                           ;AN000;;EO.
        CMP     [EXTOPEN_ON],0                   ;AN000;;EO.  error and IFS call;smr;SS Override
        JZ      error_return2                    ;AN000;;EO.  return with error
local_extopen:

        CMP     AX,error_file_not_found          ;AN000;;EO.  file not found error
        JNZ     error_return2                    ;AN000;;EO.  no,
        TEST    [EXTOPEN_FLAG],ext_nexists_create;AN000;;EO.  want to fail;smr;SS Override
        JNZ     do_creat                         ;AN000;;EO.  yes
        JMP     short extexit                    ;AN000;;EO.  yes
do_creat:
        MOV     CX,[SAVE_CX]                     ;AN000;;EO.  get ds:dx for file name;smr;SS Override
        LDS     SI,DWORD PTR [SAVE_SI]           ;AN000;;EO.  cx = attribute;smr;SS Override
        MOV     DX,SI                            ;AN000;;EO.
        invoke  $Creat                           ;AN000;;EO.  do create
        JC      extexit                          ;AN000;;EO.  error
        MOV     [EXTOPEN_FLAG],action_created_opened  ;AN000;;EO. is created/opened;smr;SS Override
        JMP     SHORT setXAttr                        ;AN000;;EO.   set XAs

ext_ok:
        CMP     [EXTOPEN_ON],0                   ;AN000;;EO.  IFS call ?;smr;SS Override
        JZ      ok_return                        ;AN000;;EO.  yes
        MOV     [EXTOPEN_FLAG],action_opened     ;AN000;;EO.  opened;smr;SS Override
setXAttr:
        PUSH    AX                      ;AN000;;EO. save handle for final
        invoke  get_user_stack          ;AN000;;EO.
        MOV     AX,[EXTOPEN_FLAG]       ;AN000;;EO.;smr;SS Override
        MOV     [SI.USER_CX],AX         ;AN000;;EO. set action code for cx
        POP     AX                      ;AN000;;EO.
        MOV     [SI.USER_AX],AX         ;AN000;;EO. set handle for ax

ok_return:                              ;AN000;
        transfer SYS_RET_OK             ;AN000;;EO.

extexit2:                               ;AN000; ERROR RECOVERY

        POP     BX                      ;AN000;EO. close the handle
        PUSH    AX                      ;AN000;EO. save error code from set XA
        CMP     [EXTOPEN_FLAG],action_created_opened    ;AN000;EO. from create;smr;SS Override
        JNZ     justopen                ;AN000;EO.
        LDS     SI,DWORD PTR [SAVE_SI]           ;AN000;EO.  cx = attribute;smr;SS Override
        LDS     DX,DWORD PTR [SI]                ;AN000;EO.
        invoke  $UNLINK                 ;AN000;EO. delete the file
        JMP     SHORT reserror          ;AN000;EO.

justopen:                               ;AN000;
        invoke  $close                  ;AN000;EO. pretend never happend
reserror:                               ;AN000;
        POP     AX                      ;AN000;EO. retore error code from set XA
        JMP     SHORT extexit           ;AN000;EO.


ext_file_unfound:                       ;AN000;
        MOV     AX,error_file_not_found ;AN000;EO.
        JMP     SHORT extexit           ;AN000;EO.
ext_inval:                               ;AN000;
        MOV     AX,error_invalid_function;AN000;EO.
extexit:
        transfer SYS_RET_ERR            ;AN000;EO.

EndProc $Extended_Open                  ;AN000;

DOSCODE ENDS
        END
