
        page    ,132
        title   V86CODE\handle.asm


;********************************************************************** ;56264;
;* 23Oct92 - G.L.Fulk                                                   ;56264;
;* Int 21h, 5700h fails to preserve AX if the handle is a DOS device    ;56264;
;* driver.                                                              ;56264;
;********************************************************************** ;56264;
;62131 Carlos A. Romero - Add EA support from Windows FM
;64846 Carlos A. Romero - Use 5714 instead of 5704
;********************************************************************** ;73258;
;* 30Aug93 - B.M.Russin                                                 ;73258;
;* Int 21h, 5700h fails to preserve AX on good return if the handle     ;73258;
;* is a file handle                                                     ;73258;
;********************************************************************** ;73258;


.xlist
include v86seg.inc
.xcref
include basemaca.inc
include osmaca.inc
include oldmaca.inc
include demmac.inc
include dossymc.inc
include filemode.inc
include opncreat.inc
include error.inc
include mi.inc
include mvdmexp.inc
include demsvc.inc
include dirent.inc
include pdb.inc
include demdbcs.inc
include devhdr.inc
include dos3dev.inc
include syscalls.inc
include extattr.inc
include struc30.inc
include demcd.inc
.cref
.list
        CpuMode 386

        ReferV86    ConSwap,BYTE
        ReferV86    ConSFT,DWORD
        ReferV86    CurrentPDB,WORD
        ReferV86    Temp_Var,WORD
        ReferV86    ExtErr_Locus,BYTE
        ReferV86    ExtErr,WORD
        ReferV86    CURDRV,BYTE

V86LoL  SEGMENT
        EXTRN   CDSADDR: Dword
V86LoL  ENDS

V86CODE SEGMENT
        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

        EXTRN   JFNFree:NEAR
        EXTRN   RMDevCheckName:NEAR
        EXTRN   RMDevAllocDI:NEAR
        EXTRN   pJFNFromHandle:NEAR
        EXTRN   Sys_Ret_OK:NEAR
        EXTRN   Sys_Ret_Err:NEAR
        EXTRN   Sys_Return:NEAR
        EXTRN   RMDevWrite:NEAR
        EXTRN   VSFNFromHandle:NEAR
        EXTRN   RMDevRead:NEAR
        EXTRN   Get_User_Stack:NEAR
        EXTRN   SFFromSFN:NEAR
        EXTRN   RMDevCloseDI:NEAR
        EXTRN   RMDevOpen:NEAR
        EXTRN   RMDevClose:NEAR
        EXTRN   $Alloc:NEAR
        EXTRN   $Dealloc:NEAR
        EXTRN   RMDevCheck:NEAR
        EXTRN   RMDevXFlags:NEAR
        EXTRN   FCB_Return:NEAR
        EXTRN   $GET_DATE:NEAR
        EXTRN   $GET_TIME:NEAR
        EXTRN   $GET_VERSION:NEAR

;***    $LSeek - move r/w pointer.  Int21 interlude to w_LSeek
;
;       Call DemChgFilePtr to perform work.  On return, stash return values
;       in proper user registers (in User_* on user stack), and do error
;       code processing in case of error.
;       This is an int21 dispatcher interlude routine.
;
;       Assembler usage:
;               MOV     DX, offsetlow
;               MOV     CX, offsethigh
;               MOV     BX, handle
;               MOV     AL, method
;               MOV     AH, LSeek
;               INT     int_command
;
;       Return:
;               NC -> no error
;                     DX:AX has the new location of the pointer
;               CY -> error
;                     AX = ERROR_INVALID_HANDLE
;                        = ERROR_INVALID_FUNCTION
;                     Fatal UserError if seek ends up at a negative location
;
;       CALLS   w_LSeek, Sys_Ret_OK, Sys_Ret_Err
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure $LSeek,NEAR
        call    VSFNFromHandle          ; Get the VSFN
        .if     nc
          call    RMDevCheck
          ;return ZF CY = file
          ;       ZF NC = OS/2 device
          ;       NZ NC = DOS device
          jnc     short lsk3              ; Skip if RM or OS/2 device
          SVC     SVC_DEMCHGFILEPTR       ; Let OS/2 do all the work for files
          .if     nc
lsk1:       call    get_user_stack
            mov     DS:[SI.User_DX],DX      ; return DX:AX
            jmp     Sys_Ret_OK              ; return A-OK
          .endif
        .endif
        jmp     Sys_Ret_Err

lsk3:   xor     ax,ax
        mov     dx,ax                   ; Return position zero for devices
        jmp     short lsk1
EndProc   $LSeek


;***    $Commit - Flush pending buffers to disk
;
;       Call DemCommit to return a handle to the system,
;       do error processing on return.
;       This is an Int21 dispatcher interlude routine.
;
;       ENTRY   BX = handle
;
;       EXIT    Carry Clear
;                       it worked
;               Carry Set
;                       AX = ERROR_INVALID_HANDLE
;
;       CALLS   DemCommit, Sys_Ret_OK, Sys_Ret_Err
;
        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure $Commit,near
        call    VSFNFromHandle          ; Get the VSFN
        .if     nc                      ; exit if bad handle
          call    RMDevCheck
          ;return ZF CY = file
          ;       ZF NC = OS/2 device
          ;       NZ NC = DOS device
          .if     c                     ; Skip if RM or OS/2 device
            SVC     SVC_DEMCOMMIT       ; Let OS/2 do all the work for files
          .endif
        .endif
        jmp     Sys_Return
EndProc $Commit


;***    $Dup - Duplicate a handle to any available handle
;
;       Call DemDup to return a handle to the system,
;       do error processing on return.
;       This is an Int21 dispatcher interlude routine.
;
;       ENTRY   BX = handle
;
;       EXIT    Carry Clear
;                   it worked
;                   AX = new handle
;               Carry Set
;                   AX = ERROR_INVALID_HANDLE
;
;       CALLS   DemDup, Sys_Ret_OK, Sys_Ret_Err
;
        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure $Dup,near
        call    Worker$Dup
        ;return NC=jump to SYS_RET_OK
        ;       CY=jump to SYS_RET_ERR
        jmp     Sys_return
EndProc $Dup

Procedure Worker$Dup,near

LocalVar  pJFN,DWORD            ; WARNING -- MUST MATCH $Dup2 FRAME EXACTLY
LocalVar  VSFN,WORD
LocalVar  VJFN,WORD
LocalVar  usOS2Dev,WORD

DupFrame    =   ?frame          ; Enforce above restriction

        EnterProc
        call    VSFNFromHandle          ; Get the VSFN
        jc      short DupEnd            ; exit if bad handle
        mov     VSFN,bx                 ; Save the VSFN
        call    short JFNFree           ; Find a free virtual handle
        jc      short DupEnd            ; Exit if none available
        mov     pJFN.Offst,di           ; Save pJFN
        mov     pJFN.Segmt,es
        mov     VJFN,bx                 ; Save new handle
;;;     jmp     short Dup_Command

;   $Dup and $Dup2 common code
;
;   VSFN = virtual SFN
;   VJFN = new handle
;   ES:DI = address of new handle entry in JFN table

Entry   Dup_Common,near,local
        mov     bx,VSFN
        call    RMDevCheck
        ;return ZF CY = file
        ;       ZF NC = OS/2 device
        ;       NZ NC = DOS device
        .if     nz
          call    RMDevOpen             ; Do the V86 open
          jc      short DupEnd
        .else
          mov     usOS2Dev,0            ; Assume OS2 file
          .if     nc                    ; Skip if OS2 file
            mov     usOS2Dev,1          ; OS2 device
          .endif
          SVC     SVC_DEMDUP            ; Do the OS2 DUP
          jc      short DupEnd          ; Skip if error

          mov     ax,usOS2Dev
          .if     <NONZERO ax>          ; skip if not OS2 device
            inc     es:[di].rmdi_refcnt ; increment reference count
          .endif
        .endif

        mov     bx,VSFN
        LES     DI,pJFN
        mov     es:[di],bl              ; Initialize the new VSFN
        mov     ax,VJFN                 ; Get new virtual handle
        clc                             ; return A-OK, new handle in AX
Entry   DupEnd,near,local
        LeaveProc NoCheck
        ret

EndProc Worker$Dup

;***    $Dup2 - $Dup - Duplicate a handle to specified handle
;
;       Close the new handle and transfer to Dup_Common to
;       perform the dup.
;
;       ENTRY   BX = handle
;               CX = new handle
;
;       EXIT    Carry Clear
;                   it worked
;                   AX = new handle
;               Carry Set
;                   AX = ERROR_INVALID_HANDLE
;
;       CALLS   Dup_Common, DupEnd
;
        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure $Dup2,near
        call    Worker$Dup2
        ;return NC=jump to SYS_RET_OK
        ;       CY=jump to SYS_RET_ERR
        jmp     Sys_return
EndProc $Dup2

Procedure Worker$Dup2,near

LocalVar  pJFN,DWORD            ; WARNING -- MUST MATCH $Dup FRAME EXACTLY
LocalVar  VSFN,WORD
LocalVar  VJFN,WORD
LocalVar  usOS2Dev,WORD

.errnz  ?frame - DupFrame       ; Enforce above restriction

        EnterProc
        .if     <BX eq CX>              ; sudeepb PTR B786581
          mov     ax,ERROR_INVALID_HANDLE
          stc
          jmp     short DupEnd
        .endif
        call    VSFNFromHandle          ; Get the VSFN
        jc      short DupEnd            ; exit if bad handle
        mov     VSFN,bx                 ; Save the VSFN
        mov     bx,cx                   ; Get the new handle
        call    pJFNFromHandle          ; Get pointer to new handle entry
        jc      short DupEnd            ; Exit if new handle is invalid
        mov     pJFN.Offst,di           ; Save pJFN
        mov     pJFN.Segmt,es
        mov     VJFN,bx                 ; Save new handle
        call    $Close                  ; Close the new handle
        jmp     short Dup_Common        ; Jump to common code

EndProc Worker$Dup2,NoCheck


subttl  $ExtHandle - extend handle count
page

;
;   Assembler usage:
;           MOV     BX, Number of Opens Allowed (MAX=65534;66535 is
;           MOV     AX, 6700H                    reserved to mark SFT
;           INT     int_command                  busy )
;
;   Error return:
;           AX = error_not_enough_memory
;                or error_too_many_open_files
;   No registers returned

ExtHandle_enlarge equ   0
ExtHandle_shrink  equ   1
ExtHandle_psp     equ   2

        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure $ExtHandle,NEAR
        xor     bp,bp                   ;BP=0 = ExtHandle_enlarge
        .if     <BX b FilPerProc>       ; BX minimum value of 20
          mov     bx,FilPerProc         ;  bx = 20
        .endif
        mov     es,[CurrentPDB]         ; get user process data block
        mov     cx,es:[PDB_JFN_Length]  ; get number of handle allowed

        .if     <BX eq CX>              ; the requested == current
          jmp     Sys_Ret_OK
        .endif
        .if     b
          ;------ request < current, therefore shrink
          mov     bp,ExtHandle_shrink
          mov     ds,word ptr es:[PDB_JFN_Pointer+2] ;
          mov     si,bx
          sub     cx,bx                 ; get difference, shrink by CX
          ;scan through the JFT skrink area looking for non-closed files
          .repeat
            .if     <<byte ptr ds:[si]> ne -1>
              error   error_too_many_open_files
            .endif
            inc     si
          .loop

          SVC     SVC_DEMEXTHANDLE        ; Shrink Real JFN too
          .if     <BX be FilPerProc>
            mov     bp,ExtHandle_psp
            mov     di,PDB_JFN_Table      ; es:di -> jfn table in psp
            push    bx
            jmp     short movhandl
          .endif
        .endif

;------ enlarge the JFTs
        .if     <BX eq -1>              ; 65535 is not allowed
          error   error_invalid_function
        .endif

        clc
        PUSH    BX                      ; save requested number
        ADD     BX,0FH                  ; adjust to paragraph boundary
        MOV     CL,4
        RCR     BX,CL                   ; DOS 4.00 fix                          ;AC000;
        AND     BX,1FFFH                ; clear most 3 bits

        PUSH    BP
        call    $ALLOC                  ; allocate memory
        POP     BP
        JC      short no_memory         ; not enough meory

        MOV     ES,AX                   ; es:di points to new table memory
        XOR     DI,DI

        POP     BX
        PUSH    BX
        SVC     SVC_DEMEXTHANDLE        ; Grow real JFN as well
        .if     c
          call    $DEALLOC
no_memory:
          pop     bx                    ; clean stack
          error   error_not_enough_memory
        .endif

movhandl:
        mov     ds,[CurrentPDB]         ; get user PDB address

        .if     <BP ne ExtHandle_enlarge>
          POP     CX                      ; cx = the amount you shrink
          PUSH    CX
        .else
          mov     cx,DS:[PDB_JFN_Length]    ; get number of old handles
        .endif
        mov     dx,cx
        LDS     SI,DS:[PDB_JFN_Pointer]   ; get old table pointer
        ASSUME  ds:NOTHING
        REP     MOVSB                   ; copy infomation to new table

        POP     CX                      ; get new number of handles
        PUSH    CX                      ; save it again
        SUB     CX,DX                   ; get the difference
        MOV     AL,-1                   ; set availability to handles
        REP     STOSB

        mov     ds,[CurrentPDB]         ; get user process data block
        .if     <<word ptr ds:[PDB_JFN_Pointer]> eq 0>
            ; check if original table ptr
          PUSH    BP
          PUSH    DS                      ; save old table segment
          PUSH    ES                      ; save new table segment
          MOV     ES,WORD PTR DS:[PDB_JFN_Pointer+2] ; get old table segment
          call    $DEALLOC                ; deallocate old table meomory
          POP     ES                      ; restore new table segment
          POP     DS                      ; restore old table segment
          POP     BP
        .endif

        .if     <BIT BP and ExtHandle_psp>
          mov     word ptr ds:[PDB_JFN_Pointer],PDB_JFN_Table   ; restore
        .else
          mov     word ptr ds:[PDB_JFN_Pointer],0 ; new table ptr offset always 0
        .endif
        MOV     WORD PTR DS:[PDB_JFN_Pointer+2],ES ; update table ptr segment
        POP     DS:[PDB_JFN_Length]      ; restore new number of handles
        jmp     Sys_Ret_OK
EndProc   $ExtHandle


;***    $Close - return a handle to the system.  Int21 interlude to DemClose
;
;       Call DemClose to return a handle to the system,
;       do error processing on return.
;       This is an Int21 dispatcher interlude routine.
;
;       ENTRY   BX = handle
;
;       EXIT    Carry Clear
;                       it worked
;               Carry Set
;                       AX = ERROR_INVALID_HANDLE
;
;       CALLS   DemClose, Sys_Ret_Err, Sys_Ret_OK, RMDevCloseDI, VSFNFromHandle
;
;       NOTE:   Jeff Harbers and Multiplan hard disk copy rely on certain
;               values when $Close succeeds even though we document it as
;               trashing AX.
;
;ANOTHERNOTE:   God knows what these certain values are.  I'm not doing
;               anything to provide them -- danno
;
;       USES:   AX,BX,CX,DX,SI,DI,ES,DS
;
        ASSUME  CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure $Close,near
        call    pJFNFromHandle          ; get address of JFN entry
        jc      short clo50             ; skip if bad handle
        movzx   bx,byte ptr es:[di]     ; get the VSFN
        cmp     bl,-1                   ; must only compare BL
        je      short clo40             ; jump if invalid handle
        mov     cx,bx                   ; (CX) = VSFN
        SaveReg <es,di>
        call    RMDevCheck              ; (BX) = real handle if OS2 dev or file
        ;return ZF CY = file
        ;       ZF NC = OS/2 device
        ;       NZ NC = DOS device
        .if     c
          SVC     SVC_DEMCLOSE          ; let OS/2 do all the work for files
        .else
          mov     ax,0                  ; no errors, yet
          .if     z                     ; skip if RM device
            SVC     SVC_DEMCLOSE        ; close OS/2 device
            .if     nc                  ; skip if error
              xor     ax,ax
            .endif
          .endif
          push    ax
          mov     bx,cx                 ; (BX) = VSFN
          call    RMDevCloseDI          ; close the DevInfo entry
          pop     cx                    ; recall OS/2 error
          .if     nc                    ; exit if RM error
            mov     ax,cx               ; else, recall OS/2 error
            cmp     ax,1
            cmc                         ; set CF if non-zero
          .endif
        .endif

        RestoreReg <di,es>
        mov     byte ptr es:[di],-1     ; remove the handle
        jc      short clo50
        mov     ah,close                ; MZ Bogus multiplan fix
        jmp     Sys_Ret_OK

clo40:  mov     ax,ERROR_INVALID_HANDLE
clo50:  jmp     Sys_Ret_Err
EndProc   $Close

;***    $File_Times - modify write time on handle.
;
;       Call FileTimes or Ext_Information to do work, set up return values,
;       do exit processing. This is an Int21 dispatcher interlude routine.
;
;       Assembler usage:
;               MOV AH, FileTimes
;               MOV AL, func
;               MOV BX, handle
;       ; if AL = 1 then next two are mandatory
;               MOV CX, time
;               MOV DX, date
;       ; if AL = 2 or 3 (query/set EAs) then next four are mandatory
;               MOV CX, cbInfoBuf
;               MOV DX, InfoLevel
;               LDS SI, asciiz filename (if handle = -1)
;               LES DI, InfoBuf
;               INT 21h
;         if AL = 14H (copy EAs) then next four are mandatory
;               MOV CX, size of EAs
;               MOV BX, source handle
;               MOV DX, source handle
;
;       ; if AL = 0 then CX/DX has the last write time/date
;       ; for the handle.
;
;       Error returns:
;               AX = ERROR_INVALID_FUNCTION
;                  = ERROR_INVALID_HANDLE
;
        ASSUME  DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure $File_Times,near

;------ int 21h, ax=5714h
        .if     <al eq 14h>             ;defect 64846 - use 14 instead of 04
          ;Add support for EAs from Windows FM                          ;62131;
          call    VSFNFromHandle        ; Get the VSFN for source handle;62131;
          jc      short ftm3_err        ; exit if bad handle            ;62131;
          xchg    dx, bx                ; save BX & set up target handle;62131;
          call    VSFNFromHandle        ; Get the VSFN for source       ;62131;
          jc      short ftm3_err        ; exit if bad handle            ;62131;
          xchg    dx, bx                ; now bx-dx have real handles   ;62131;
          SVC     SVC_DEMCOPYEAS                                        ;62131;
          jmp     Sys_Return                                            ;62131;
        .endif

;------ AL>=2 ax=5702 ax=5703
        .if     <AL ae 2>
          .if     <BX ne -1>
            call    VSFNFromHandle      ; Get the VSFN
            jc      short ftm3_err      ; exit if bad handle
          .endif
          SVC     SVC_DEMEXTINFO
          jmp     Sys_Return
        .endif

;------ AL<2 ax=5700 ax=5701                                            ;73258;
        call    VSFNFromHandle          ; Get the VSFN
        jc      short ftm3_err          ; exit if bad handle
        call    RMDevCheck
        ;return ZF CY = file
        ;       ZF NC = OS/2 device
        ;       NZ NC = DOS device
        .if     z                       ; Skip if a real-mode device
          push    ax                                                    ;73258;
          SVC     SVC_DEMFILETIMES      ; Let OS/2 do all the work for files
          .if     c                                                     ;73258;
            pop   dx                                                    ;73258;
ftm3_err:   jmp   Sys_Ret_Err                                           ;73258;
          .endif                                                        ;73258;
          pop     ax                                                    ;73258;
ftm2:
          call    get_user_stack
          mov     [SI.User_CX],CX       ; time
          mov     [SI.User_DX],DX       ; date
          jmp     Sys_Ret_OK            ; return A-OK

        .endif
        ;------ DOS device driver handle:
;       xor     ax,ax                   ; it's a DOS device
;       mov     cx,ax
;       mov     dx,ax                   ; zero date and time

        .if     <al eq 0>       ;We only want to take action for a
                                ;"get" date/time.  Ignore "set" date/time.
          push    ax                                                    ;56264;

          call    $GET_DATE
          ;return CX=year (1980...)
          ;       DH=month (1..12)
          ;       DL=day (1..31)
          ;packed date format = yyyy yyym mmmd dddd

          sub     cx,1980         ; CH now contains the year as a
          mov     ch,cl           ; 7 bit value = 0yyy yyyy

          mov     cl,dh           ; CL now contains the month as a
          shl     cl,4            ; 4 bit value = mmmm 0000

                                  ; CX=CH||CL=0yyy yyyy mmmm 0000

          shl     cx,1            ; CX=yyyy yyyym mmm0 0000
                                  ; DL contains the day as a 5 bit
                                  ; value=000d dddd

          or      cl,dl           ; CX now contains the date in packed
          push    cx              ; format.  Save it away.

          call    $GET_TIME
          ;return CH = hour (0..23)
          ;       CL = minute (0..59)
          ;       DH = second (0..59)
          ;       DL = hundredths (0..99)
          ;packed time format = hhhh hmmm mmms ssss

          shl     cl,2            ; CL now contains the minute as a
                                  ; 6 bit value = mmmm mm00

          shl     cx,3            ; CX now contains the hour and
                                  ; minute values = hhhh hmmm mmm0 0000

          shr     dh,1            ; DH contains the seconds as an even
                                  ; 5 bit value = 000s ssss
                                  ; Seconds can't be stored with all
                                  ; 60 seconds of resolution because
                                  ; that would require 6 bits, which
                                  ; we don't have space for.

          or      cl,dh           ; CX now has the packed time.

          pop     dx              ; DX now has the packed date.
          pop     ax                                                      ;56264;
        .endif
        clc     ;NC = indicates no error
        jmp     short ftm2

EndProc   $File_Times

;***    $Read - Read from a file handle.  Int21 interlude to w_Read
;
;       Call DemRead to read data into user space, do error
;       processing on return.
;       This is an Int21 dispatcher interlude routine.
;
;       ENTRY   BX    =  handle
;               CX    =  count
;               DS:DX -> buffer
;
;       EXIT    Carry Clear
;                       it worked (must still check ax)
;                       AX has number of bytes read
;               Carry Set
;                       it didn't work
;                       AX = ERROR_INVALID_HANDLE
;                          = ERROR_ACCESS_DENIED
;
;       CALLS
;           DemRead
;           Sys_Ret_OK
;           Sys_Ret_Err
;           VSFNFromHandle
;           RMDevCheck
;           RMDevRead
;
        ASSUME      CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure $Read,near
        call    VSFNFromHandle          ; get the VSFN
        .if     nc                      ; exit if bad handle
          call    RMDevCheck
          ;return ZF CY = file
          ;       ZF NC = OS/2 device
          ;       NZ NC = DOS device
          .if     z
            SVC     SVC_DEMREAD         ; files & OS/2 devices
          .else
            call    RMDevRead           ; DOS devices
          .endif

          .if     nc
            jmp     Sys_Ret_OK              ; return A-OK
          .endif
        .endif
        jmp     Sys_Ret_Err

EndProc   $Read

;***LP  $Write - write to a file.  Int21 interlude to DemWrite
;
;       ENTRY
;           BX
;               file handle
;           CX
;               number of bytes to write
;           DS:DX
;               Address of data to write
;
;       EXIT
;           SUCCESS
;               AX = # of bytes written
;               exit through Sys_Ret_OK
;           FAILURE
;               exit through Sys_Ret_Err
;
;       CONTEXT
;           Executes in V86 mode
;
;       STEPS
;
;       NOTES
;           None
;
;       WARNINGS
;           None
;
;       CALLS
;
;       USES
;           AX,BX,CX,DX,SI,DI,DS,ES

        ASSUME CS:V86GROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING

Procedure $Write,near
        call    VSFNFromHandle          ; get the VSFN
        .if     nc                      ; exit if bad handle
          call    RMDevCheck
          ;return ZF CY = file
          ;       ZF NC = OS/2 device
          ;       NZ NC = DOS device
          .if     z
            SVC     SVC_DEMWRITE        ; files & OS/2 devices
          .else
            call    RMDevWrite          ; DOS devices
          .endif

          .if     nc
            jmp     Sys_Ret_OK          ; return A-OK
          .endif
        .endif
        jmp     Sys_Ret_Err
EndProc     $Write


V86CODE ENDS

        END
