;       SCCSID = @(#)dosio.asm  6.5 91/08/26
;       SCCSID = @(#)dosio.asm  6.5 91/08/26
        page    ,132
        .386p

        TITLE   dosIO - DOSREAD and DOSWRITE interfaces to VIO/KBD

;***
;       WARNING. If modifying this file, may also have to modify
;       h\bseerr.h
;       src\cmd\bind\api\d_eclass.asm
;
;       DOSIO - implement the visible API for user applications that
;       correctly interface with VIO and KBD.
;
;       Since we have the requirement that the VIO/KBD routine must occur to
;       ring 3 code, and that the DOSREAD/DOSWRITE API go through VIO/KBD
;       when the specified handle is routed to CON, this implies that DOSREAD
;       and DOSWRITE exist as ring 3 code and, hence, why they appear in the
;       DOSCALL1 dynlink library.
;
;       The routine of DOSREAD/DOSWRITE to VIO/KBD is performed by detecting
;       a special error code returned by the internal DOSIREAD/ DOSIWRITE
;       routines when given an handle to CON.  Based on this special error,
;       we route to the VIO/KBD routines.
;
;       The only subtle piece is that DOSREAD will read an entire line of
;       text from the console and deblock it into the user's space.  This
;       requires us to maintain a per-process data segment that contains the
;       unread portion of the input command line.
;
;
;       Modification History:
;
;       21-Apr-1986     MJZ     DOSREAD will correctly tack on CR/LF to
;                               buffer and echo LF to console when calling
;                               KBDSTRINGIN.
;
;       10-Jun-1986     RJS     DOSREAD fixed to handle ctrl-z as EOF in
;                               cooked mode. Also fixed to read the specified
;                               number of characters in RAW mode.
;
;       12-Aug-1986     MZ      Change parameters to KBDGETSTATUS to use default
;                               logical keyboard handle of 0 and not the user's
;                               input handle.
;
;       29-Oct-1986     djg     Added RAS traceing.
;
;       18-Nov-1986     KCD     Change length parameter to KBDSTRINGIN to
;                               use two word structure.
;
;       20-Nov-1986     TPL     Supply template length on KBDSTRINGIN while
;                               keyboard is cooked.
;
;       19-Apr-1988     JSF     Move error table to separate include file
;                               1.1 PTM 1885
;
;       03-Aug-1992     Carlos Romero - Change Team
;                       #25355 remove call to VIO32SETANSI from DOS32WRITE
;
;       11/18/93        noto    Define which segment for Page Tuning DLL

.xlist
INCL_ERRORS     equ     1
        include bseerr.inc
        include basemaca.inc
        include realmac.inc
        include infoseg.inc
        include dc1segs.inc                                     ; DCR 704
        include dc1mmpha.inc

;       NOTE File errtab.inc is also included further on in the file.

.list

EXTRN   DOSALLOCSEG:FAR
EXTRN   DOSIREAD:FAR
EXTRN   DOSIWRITE:FAR
EXTRN   DOSIPROTECTREAD:FAR
EXTRN   DOSIPROTECTWRITE:FAR
EXTRN   DOS32IQUERYFHSTATE:FAR
EXTRN   DOS32IPROTECTQUERYFHSTATE:FAR


EXTRN   KBDPEEK:FAR             ; settable kbd peek address             ;MDLL
EXTRN   KBDGETSTATUS:FAR        ; settable get status address           ;MDLL
EXTRN   KBDSTRINGIN:FAR         ; settable string input address         ;MDLL
EXTRN   VIOWRTTTY:FAR           ; settable string output                ;MDLL



; Defining this segment and using the ASSUME statement turns on the
; masm switch to allow the FLAT keyword.

CODE16C_TUNE2  SEGMENT DWORD USE16 PUBLIC 'CODE'
CODE16C_TUNE2  ENDS

CODE32_TUNE2  SEGMENT DWORD USE32 PUBLIC 'CODE'
CODE32_TUNE2  ENDS


CODE32  SEGMENT DWORD USE32 PUBLIC 'CODE'                       ; DCR 704

EXTRN   DOS32IWRITE:NEAR
EXTRN   DOS32IREAD:NEAR
EXTRN   DOS32IPROTECTWRITE:NEAR
EXTRN   DOS32IPROTECTREAD:NEAR

EXTRN   KBD32CHARIN:NEAR
EXTRN   KBD32STRINGIN:NEAR
EXTRN   VIO32WRTTTY:NEAR

;defect# 25355  start - remove next line
;EXTRN   VIO32SETANSI:NEAR

CODE32  ENDS


DATA32  SEGMENT                                                 ; DCR 704
DATA32  ENDS

;
; Dos32IQueryFHState status bits we need
;

FHSTATE_CON     equ 00080000h
FHSTATE_RAWMODE equ 00100000h
FHSTATE_EOF     equ 00200000h

;
; KbdChar structure
;

KbdChar     struc
        char_code           db      ?   ; ASCII code
        char_scan           db      ?   ; extended (scan) code
        char_status         db      ?   ;
        char_nls_status     db      ?   ;
        char_shift_state    dw      ?   ;
        char_time_stamp     dd      ?   ;
KbdChar     ends

KBDCHAR_FINAL   equ 01000000B           ; TRUE => char exists

;
; KbdStatus structure
;

KbdStatus struc
        slength             dw      ?   ; length of data structure
        bit_mask            dw      ?   ; bit mask
        turn_around_char    dw      ?   ; turnaround character
        interim_char_flags  dw      ?   ; interim character flags
        shift_state         dw      ?   ; shift state
KbdStatus ends

KBDSTATUS_RAW   equ 0000000000000100B   ; TRUE => raw mode

;
; The KbdLen structure is use with the KBDSTRINGIN call.
;

KbdLen  struc
        input_len           dw      ?   ; input buffer length
        receive_len         dw      ?   ; received input length
KbdLen  ends

IOWAIT  equ     0
INPSIZ  equ     254                     ; largest input KBDSTRINGIN supports
                                        ; (including CR byte at end)
BUFSIZ  equ     INPSIZ+1                ; plus 1 more byte so we can append LF


DATA    SEGMENT                                                 ; DCR 704


; KbdLenBuf and KbdCharBuf appear here instead of as LocalVars in the
; appropriate routines strictly because we want structures that are guaranteed
; to not cross a 64k boundary.  This relieves our KBDSTRINGIN thunk
; (KBD32STRINGIN) from having to validate the addresses and move the structures
; as need be,

KbdLenBuf   KbdLen  <>
KbdCharBuf  KbdChar <>
KbdInputBuf DB      BUFSIZ dup (?)      ; buffer
LF          DB      0Ah                 ; linefeed to advance to next line

; Here is a slightly strange trick. Both the 16-bit and 32-bit versions
; of DOSREAD need to maintain the pointer into the KBD buffer inputbuf.
; Since the offset into the data segment is the same as the lower 16-bits
; of the flat pointer, then the pNext and pEnd pointers are defined to be
; only the low 16-bits of the flat pointer.

pNext   LABEL WORD                      ; pointer to next avail char in buffer
pNext32 DD OFFSET FLAT:KbdInputBuf      ; 32 bit version of pointer
        .errnz (pNext - pNext32)        ;Better be pointing at same object

pEnd    LABEL WORD                      ; pointer beyond last avail char
pEnd32  DD OFFSET FLAT:KbdInputBuf      ; 32 bit version of pointer
        .errnz (pEnd - pEnd32)          ;Better be pointing at same object

DATA    ENDS


CODE16C_TUNE2    SEGMENT                                        ; DCR 704
        assume  cs:CODE16C_TUNE2, ds:nothing, es:nothing, ss:nothing

;**     DOSREAD - read a specified number of bytes from a specified handle
;
;       DOSREAD is used to transfer bytes from a file/device into the user's
;       address space.  In the case of reading from the console, we use
;       KBDSTRINGIN to read into a local buffer and then deblock it out from
;       there.
;
;       ENTRY:              WORD    file handle
;                           DWORD   transfer address
;                           WORD    number to transfer
;               SS:SP ->    DWORD   pointer to location to return number read
;
;       EXIT:   If successful then
;                   AX = 0
;                   number of bytes read is filled in
;               if failure then
;                   AX = error code
;       USES:   FLAGS
;

procedure   DOSREAD,FAR

ArgVar      pCount,DWORD
ArgVar      count,WORD
ArgVar      pData,DWORD
ArgVar      h,WORD

LocalVar    KbdStatusBuf,<SIZE KbdStatus>

        EnterProc

;       Most efficient:  Try to call the internal dos routine first.  If
;       we get the peculiar error, then we need to do the KBD stuff.

;       MMIOPH Performance Hook Removed
;       Maj/Min = 300BH, Data = h (file handle)

        push    si
        push    di

        push    h                       ; file handle
        push    pDatah                  ; segment of transfer
        push    pDatal                  ; offset of transfer
        push    count                   ; number of bytes to read
        push    pCounth                 ; segment of returned count
        push    pCountl                 ; offset of returned count

        call    DOSIREAD                ; call the internal read routine
        pop     di
        pop     si

        cmp     ax,error_VIOKBD_request ; do we need to call KBD?
        jnz     drd9                    ; no, go return what we have

; Preserve all necessary registers
        SaveReg <bx,cx,dx,si,di,ds,es>

; Need to use internal buffer to read chars via KBDSTRINGIN

        mov     ax,data
        mov     ds,ax                   ; gain data segment addressability
        assume  ds:data

; See if there is buffered data.  If not, fill the internal buffer

        mov     si,pNext                ; get pointer to next char
        cmp     si,pEnd                 ; is it = to end (no chars in buffer)
        jnz     drd2                    ; no, go copy out data
;
; Get the status of the keyboard to see if we are in RAW or COOKED mode.
;
        mov     [KbdStatusBuf].slength,size KbdStatus
        lea     ax,KbdStatusBuf
        push    ss
        push    ax
        push    0                       ; Handle of keyboard
        call    KBDGETSTATUS                                            ;MDLL
        or      ax,ax
        jnz     drd8                    ; return if any error

        mov     ax,INPSIZ               ; fill buffer entirely
        mov     cx,KbdLenBuf.receive_len; Get previous byte count

        test    word ptr [KbdStatusBuf].bit_mask,KBDSTATUS_RAW
        jz      short drd0
        mov     ax,count                ; read only specified number of chars
                                        ; if in raw mode
        sub     cx,cx                   ; Clear template length

; Fill the buffer by calling KBDSTRINGIN.  If any error occurs, return it
; without changing internal data

drd0:   push    ds
        push    offset KbdInputBuf
        mov     KbdLenBuf.input_len,ax  ; Set number of characters to read
        mov     KbdLenBuf.receive_len,cx; Set template length

        push    ds                      ; segment of count to read/returned
        push    offset KbdLenBuf        ; offset of count
        push    IOWAIT                  ; wait for chars to be typed
        push    0                       ; keyboard handle (unused/reserved)
        call    KBDSTRINGIN             ; go do operation               ;MDLL

        or      ax,ax                   ; any errors?
        jnz     short drd8              ; yes, go return them

        mov     di,offset KbdInputBuf
        mov     pNext,di                ; pNext = 0 -> first char in buffer
        add     di,KbdLenBuf.receive_len; di   -> end of string
        mov     pEnd,di                 ; pEnd -> end of string (temporary)
;
; If we are in RAW mode, we return all the characters as read, with no special
; treatment
;
        test    word ptr [KbdStatusBuf].bit_mask,KBDSTATUS_RAW
        jnz     short drd2              ; RAW mode - return chars as read
;
; See if ctrl-z was entered. If it was the first character, we flush the
; buffer and set count to 0. If it was not the first character, we throw
; away anything else that was entered after the ctrl-z (including the ctrl-z)
;
        push    ds
        pop     es
        mov     di,offset KbdInputBuf   ; es:di -> input buffer
        cmp     byte ptr es:[di],26     ; 26 = ctrl-z
        je      short drd1              ; flush buffer if ctrl-z is first char

        mov     ax,KbdLenBuf.receive_len; ax = count read
        add     di,ax                   ; es:di -> end of buffer
        mov     word ptr es:[di],0Ah SHL 8 + 0Dh
                                        ; drop in CRLF to look like file
        add     di,2                    ; adjust count of number of chars

drd1:   mov     KbdLenBuf.receive_len,ax; Save template length
        mov     pEnd,di                 ; last is one beyond entered text

; KBDSTRINGIN left the cursor at the left margin.  Echo a line feed.

        push    ds                      ; segment of display byte
        push    offset LF               ; offset of line feed
        push    1                       ; only one byte to write
        push    0                       ; default VIO handle
        call    VIOWRTTTY               ; let VIO figure out how to output it MDLL

; Copy data from buffer into user's address space

drd2:   les     di,pData                ; es:di point to users data area
        mov     si,pNext                ; KbdInputBufseg:si point to next data

        mov     cx,pEnd                 ; get pointer to end
        sub     cx,si                   ; cx = number in buffer

        cmp     cx,count                ; is number in buffer < number requested?
        jb      short drd3              ; yes, go copy them
        mov     cx,count                ; only transfer what was requested
drd3:   mov     ax,cx                   ; save away count of transferred

;       DS:SI point to local buffer
;       ES:DI point to user's buffer
;       CX is number of bytes to move

        cld
        rep     movsb                   ; move all the requested bytes

        mov     pNext,si                ; advance over transferred data

        lds     si,pCount               ; point to count moved
        assume  ds:NOTHING
        mov     [si],ax                 ; return count moved to user
        xor     ax,ax                   ; no error

;       AX has error code
;       restore registers

drd8:   RestoreReg  <es,ds,di,si,dx,cx,bx>
        assume  ds:nothing
drd9:
;       MMIOPH Performance Hook Removed
;       Maj/Min = 308BH, Data = [pCount], ax (bytes read, rc)

        LeaveProc
        ret     ?aframe

EndProc DOSREAD

CODE16C_TUNE2  ENDS




CODE    SEGMENT
        assume  cs:code, ds:nothing, es:nothing, ss:nothing

;**     DOSWRITE - write a specified number of bytes to a specified handle
;
;       DOSWRITE is used to transfer bytes to a file/device from the user's
;       address space.  In the case of writing to the console, we use
;       VIOWRTTTY to write to the screen
;
;       ENTRY:      WORD    file handle
;                   DWORD   transfer address
;                   WORD    number to transfer
;       SS:SP ->    DWORD   pointer to location to return number written
;
;       EXIT:   If successful then
;                   AX = 0
;                   number of bytes written is filled in
;               if failure then
;                   AX = error code
;       USES:   FLAGS
;

procedure   DOSPWRITE,FAR

ArgVar      pCount,DWORD
ArgVar      count,WORD
ArgVar      pData,DWORD
ArgVar      h,WORD

        EnterProc

;       Call the internal DOS write handle code to see if we can get it
;       to the file/device quickly

;       MMIOPH Performance Hook Removed
;       Maj/Min = 3012H, Data = h (file handle)

        push    si
        push    di

        push    h                       ; file handle
        push    pDatah                  ; segment of transfer
        push    pDatal                  ; offset of transfer
        push    count                   ; number of bytes to write
        push    pCounth                 ; segment of returned count
        push    pCountl                 ; offset of returned count
        call    DOSIWRITE               ; call the internal write routine
        pop     di
        pop     si

        cmp     ax,error_VIOKBD_request ; do we redirect to the VIO package?
        jnz     short dw9               ; no, all done

;       We received the must-use-VIO error
;       Call VIO WRITE TTY to blast out the request

        SaveReg <bx,cx,dx,si,ds>        ; save all necessary registers
        mov     ax,data
        mov     ds,ax
        assume  ds:data

        push    pDatah                  ; segment of data buffer
        push    pDatal                  ; offset of data buffer
        push    count                   ; number of bytes to output
        push    0                       ; vio handle (unused/reserved)
        call    VIOWRTTTY               ; call the VIO package          ;MDLL

        or      ax,ax                   ; was there an error
        jnz     short dw8               ; yes, go return it

        lds     si,pCount               ; point at count successfully written
        mov     cx,count                ; get count requested
        mov     [si],cx                 ; say that we did it

dw8:    RestoreReg  <ds,si,dx,cx,bx>
        assume  ds:nothing

dw9:
;       MMIOPH Performance Hook Removed
;       Maj/Min = 3092H, Data = [pCount], ax (bytes read, rc)

        LeaveProc
        ret     ?aframe
EndProc DOSPWRITE

        assume  cs:code, ds:nothing, es:nothing, ss:nothing

;**     DOSPROTECTREAD - read a specified number of bytes from a specified handle
;
;       DOSPROTECTREAD is used to transfer bytes from a file/device into the
;       user's address space.  In the case of reading from the console, we use
;       KBDSTRINGIN to read into a local buffer and then deblock it out from
;       there.
;
;       ENTRY:              WORD    file handle
;                           DWORD   transfer address
;                           WORD    number to transfer
;               SS:SP ->    DWORD   pointer to location to return number read
;                           DWORD   file handle lockID
;
;       EXIT:   If successful then
;                   AX = 0
;                   number of bytes read is filled in
;               if failure then
;                   AX = error code
;       USES:   FLAGS
;

procedure   DOSPROTECTREAD,FAR

ArgVar      fhandleLockID,DWORD
ArgVar      pCount,DWORD
ArgVar      count,WORD
ArgVar      pData,DWORD
ArgVar      h,WORD

LocalVar    KbdStatusBuf,<SIZE KbdStatus>

        EnterProc

;       Most efficient:  Try to call the internal dos routine first.  If
;       we get the peculiar error, then we need to do the KBD stuff.

;       MMIOPH Performance Hook Removed
;       Maj/Min = 300BH, Data = h (file handle)

        push    si
        push    di

        push    h                       ; file handle
        push    pDatah                  ; segment of transfer
        push    pDatal                  ; offset of transfer
        push    count                   ; number of bytes to read
        push    pCounth                 ; segment of returned count
        push    pCountl                 ; offset of returned count
        push    fhandleLockIDh          ; file handle lockID high word
        push    fhandleLockIDl          ; file handle lockID low word

        call    DOSIPROTECTREAD         ; call the internal read routine
        pop     di
        pop     si

        cmp     ax,error_VIOKBD_request ; do we need to call KBD?
        jnz     pdrd9                   ; no, go return what we have

; Preserve all necessary registers
        SaveReg <bx,cx,dx,si,di,ds,es>

; Need to use internal buffer to read chars via KBDSTRINGIN

        mov     ax,data
        mov     ds,ax                   ; gain data segment addressability
        assume  ds:data

; See if there is buffered data.  If not, fill the internal buffer

        mov     si,pNext                ; get pointer to next char
        cmp     si,pEnd                 ; is it = to end (no chars in buffer)
        jnz     pdrd2                   ; no, go copy out data
;
; Get the status of the keyboard to see if we are in RAW or COOKED mode.
;
        mov     [KbdStatusBuf].slength,size KbdStatus
        lea     ax,KbdStatusBuf
        push    ss
        push    ax
        push    0                       ; Handle of keyboard
        call    KBDGETSTATUS                                            ;MDLL
        or      ax,ax
        jnz     pdrd8                   ; return if any error

        mov     ax,INPSIZ               ; fill buffer entirely
        mov     cx,KbdLenBuf.receive_len; Get previous byte count

        test    word ptr [KbdStatusBuf].bit_mask,KBDSTATUS_RAW
        jz      short pdrd0
        mov     ax,count                ; read only specified number of chars
                                        ; if in raw mode
        sub     cx,cx                   ; Clear template length

; Fill the buffer by calling KBDSTRINGIN.  If any error occurs, return it
; without changing internal data

pdrd0:  push    ds
        push    offset KbdInputBuf
        mov     KbdLenBuf.input_len,ax  ; Set number of characters to read
        mov     KbdLenBuf.receive_len,cx; Set template length

        push    ds                      ; segment of count to read/returned
        push    offset KbdLenBuf        ; offset of count
        push    IOWAIT                  ; wait for chars to be typed
        push    0                       ; keyboard handle (unused/reserved)
        call    KBDSTRINGIN             ; go do operation               ;MDLL

        or      ax,ax                   ; any errors?
        jnz     short pdrd8             ; yes, go return them

        mov     di,offset KbdInputBuf
        mov     pNext,di                ; pNext = 0 -> first char in buffer
        add     di,KbdLenBuf.receive_len; di   -> end of string
        mov     pEnd,di                 ; pEnd -> end of string (temporary)
;
; If we are in RAW mode, we return all the characters as read, with no special
; treatment
;
        test    word ptr [KbdStatusBuf].bit_mask,KBDSTATUS_RAW
        jnz     short pdrd2             ; RAW mode - return chars as read
;
; See if ctrl-z was entered. If it was the first character, we flush the
; buffer and set count to 0. If it was not the first character, we throw
; away anything else that was entered after the ctrl-z (including the ctrl-z)
;
        push    ds
        pop     es
        mov     di,offset KbdInputBuf   ; es:di -> input buffer
        cmp     byte ptr es:[di],26     ; 26 = ctrl-z
        je      short pdrd1             ; flush buffer if ctrl-z is first char

        mov     ax,KbdLenBuf.receive_len; ax = count read
        add     di,ax                   ; es:di -> end of buffer
        mov     word ptr es:[di],0Ah SHL 8 + 0Dh
                                        ; drop in CRLF to look like file
        add     di,2                    ; adjust count of number of chars

pdrd1:  mov     KbdLenBuf.receive_len,ax; Save template length
        mov     pEnd,di                 ; last is one beyond entered text

; KBDSTRINGIN left the cursor at the left margin.  Echo a line feed.

        push    ds                      ; segment of display byte
        push    offset LF               ; offset of line feed
        push    1                       ; only one byte to write
        push    0                       ; default VIO handle
        call    VIOWRTTTY               ; let VIO figure out how to output it MDLL

; Copy data from buffer into user's address space

pdrd2:  les     di,pData                ; es:di point to users data area
        mov     si,pNext                ; KbdInputBufseg:si point to next data

        mov     cx,pEnd                 ; get pointer to end
        sub     cx,si                   ; cx = number in buffer

        cmp     cx,count                ; is number in buffer < number requested?
        jb      short pdrd3             ; yes, go copy them
        mov     cx,count                ; only transfer what was requested
pdrd3:  mov     ax,cx                   ; save away count of transferred

;       DS:SI point to local buffer
;       ES:DI point to user's buffer
;       CX is number of bytes to move

        cld
        rep     movsb                   ; move all the requested bytes

        mov     pNext,si                ; advance over transferred data

        lds     si,pCount               ; point to count moved
        assume  ds:NOTHING
        mov     [si],ax                 ; return count moved to user
        xor     ax,ax                   ; no error

;       AX has error code
;       restore registers

pdrd8:  RestoreReg  <es,ds,di,si,dx,cx,bx>
        assume  ds:nothing
pdrd9:
;       MMIOPH Performance Hook Removed
;       Maj/Min = 308BH, Data = [pCount], ax (bytes read, rc)

        LeaveProc
        ret     ?aframe

EndProc DOSPROTECTREAD


;**     DOSPROTECTWRITE - write a specified number of bytes to a specified handle
;
;       DOSPROTECTWRITE is used to transfer bytes to a file/device from the user's
;       address space.  In the case of writing to the console, we use
;       VIOWRTTTY to write to the screen
;
;       ENTRY:      WORD    file handle
;                   DWORD   transfer address
;                   WORD    number to transfer
;       SS:SP ->    DWORD   pointer to location to return number written
;                   DWORD   file handle lockID
;
;       EXIT:   If successful then
;                   AX = 0
;                   number of bytes written is filled in
;               if failure then
;                   AX = error code
;       USES:   FLAGS
;

procedure   DOSPPROTECTWRITE,FAR

ArgVar      fhandleLockID,DWORD
ArgVar      pCount,DWORD
ArgVar      count,WORD
ArgVar      pData,DWORD
ArgVar      h,WORD

        EnterProc

;       Call the internal DOS write handle code to see if we can get it
;       to the file/device quickly

;       MMIOPH Performance Hook Removed
;       Maj/Min = 3012H, Data = h (file handle)

        push    si
        push    di

        push    h                       ; file handle
        push    pDatah                  ; segment of transfer
        push    pDatal                  ; offset of transfer
        push    count                   ; number of bytes to write
        push    pCounth                 ; segment of returned count
        push    pCountl                 ; offset of returned count
        push    fhandleLockIDh          ; file handle lockID high word
        push    fhandleLockIDl          ; file handle lockID low word

        call    DOSIPROTECTWRITE        ; call the internal write routine
        pop     di
        pop     si

        cmp     ax,error_VIOKBD_request ; do we redirect to the VIO package?
        jnz     short pdw9              ; no, all done

;       We received the must-use-VIO error
;       Call VIO WRITE TTY to blast out the request

        SaveReg <bx,cx,dx,si,ds>        ; save all necessary registers
        mov     ax,data
        mov     ds,ax
        assume  ds:data

        push    pDatah                  ; segment of data buffer
        push    pDatal                  ; offset of data buffer
        push    count                   ; number of bytes to output
        push    0                       ; vio handle (unused/reserved)
        call    VIOWRTTTY               ; call the VIO package          ;MDLL

        or      ax,ax                   ; was there an error
        jnz     short pdw8              ; yes, go return it

        lds     si,pCount               ; point at count successfully written
        mov     cx,count                ; get count requested
        mov     [si],cx                 ; say that we did it

pdw8:   RestoreReg  <ds,si,dx,cx,bx>
        assume  ds:nothing

pdw9:
;       MMIOPH Performance Hook Removed
;       Maj/Min = 3092H, Data = [pCount], ax (bytes read, rc)

        LeaveProc
        ret     ?aframe
EndProc DOSPPROTECTWRITE


;**     DOSQUERYFHSTATE - Interface between Dos32QueryFHState (32-bit, ring3)
;                     and Dos32IQueryFHState (16-bit, ring 0, despite its name)
;
;       DOSQUERYFHSTATE simply calls the 16-bit ring 0 entry point, and
;       returns, with exactly one small wrinkle:  if the kernel returns the
;       console and I/O mode (raw) bits set, then we call KbdPeek to determine
;       the current value for the EOF bit;  otherwise, we just set it to zero.
;
;       ENTRY:      WORD    file handle
;       SS:SP ->    DWORD   pointer to dword to return handle status bits
;
;       EXIT:   If successful then
;                   AX = 0
;               if failure then
;                   AX = error code
;       USES:   AX, CX, DX, FLAGS
;

procedure   DOSQUERYFHSTATE,FAR

ArgVar      pFlags,DWORD
ArgVar      h,WORD
LocalVar    DIsave,WORD
LocalVar    SIsave,WORD

        EnterProc

;       Call the internal DOS entry point

        mov     DIsave, di              ; save regs across filehandlelockid
        mov     SIsave, si              ; functionality

        push    h                       ; file handle
        push    pFlagsh                 ; segment of returned flags
        push    pFlagsl                 ; offset of returned flags

        call    DOS32IQUERYFHSTATE      ; call the internal query routine
        mov     di, DIsave
        mov     si, SIsave

        or      ax,ax                   ; error?
        jnz     short dq9               ; yes, don't bother checking flags

        SaveReg <di,ds,es>
        les     di,pFlags               ; check for console handle in raw mode
        mov     al,byte ptr es:[di+2]
        and     al,(FHSTATE_CON or FHSTATE_RAWMODE) shr 16
        cmp     al,(FHSTATE_CON or FHSTATE_RAWMODE) shr 16
        jne     short dq8               ; leave EOF bit alone (zero)

        mov     ax,data
        mov     ds,ax                   ; gain data segment addressability
        assume  ds:data

;  If there is buffered data, leave EOF bit clear

        mov     si,pNext                ; get pointer to next char
        cmp     si,pEnd                 ; is it = to end (no chars in buffer)
        jnz     dq8                     ; no, go copy out data

;       We need to call KbdPeek to determine the value of the console EOF bit

        push    ds                      ; segment of char buffer
        push    offset KbdCharBuf       ; offset of char buffer
        push    0                       ; keyboard handle (unused/reserved)
        call    KBDPEEK                 ; go do operation               ;MDLL
        or      ax,ax                   ; was there an error
        jnz     short dq8               ; yes, go return it

        test    [KbdCharBuf].char_status,KBDCHAR_FINAL
        jnz     short dq8               ; character is available
        or      byte ptr es:[di+2],(FHSTATE_EOF shr 16)

dq8:    sub     ax,ax                   ; return success
        RestoreReg  <es,ds,di>
        assume  ds:nothing
dq9:
;       MMIOPH Performance Hook Removed
;       Maj/Min = 3092H, Data = [pCount], ax (bytes read, rc)

        LeaveProc
        ret     ?aframe
EndProc DOSQUERYFHSTATE


;**     DOSPROTECTQUERYFHSTATE - Interface between Dos32ProtectQueryFHState (32-bit, ring3)
;                     and Dos32IProtectQueryFHState (16-bit, ring 0, despite its name)
;
;       DOSPROTECTQUERYFHSTATE simply calls the 16-bit ring 0 entry point, and
;       returns, with exactly one small wrinkle:  if the kernel returns the
;       console and I/O mode (raw) bits set, then we call KbdPeek to determine
;       the current value for the EOF bit;  otherwise, we just set it to zero.
;
;       ENTRY:      WORD    file handle
;       SS:SP ->    DWORD   pointer to dword to return handle status bits
;       SI:DI ->    DWORD   file handle lockID
;
;       EXIT:   If successful then
;                   AX = 0
;               if failure then
;                   AX = error code
;       USES:   AX, CX, DX, FLAGS
;

procedure   DOSPROTECTQUERYFHSTATE,FAR

ArgVar      FileHandleLockID,DWORD
ArgVar      pFlags,DWORD
ArgVar      h,WORD
LocalVar    DIsave,WORD
LocalVar    SIsave,WORD

        EnterProc

;       Call the internal DOS entry point

        mov     DIsave, di              ; save regs across filehandlelockid
        mov     SIsave, si              ; functionality

        push    h                       ; file handle
        push    pFlagsh                 ; segment of returned flags
        push    pFlagsl                 ; offset of returned flags
        push    FileHandleLockIDh       ; filehandle lockID highword
        push    FileHandleLockIDl       ; filehandle lockID lowword

        call    DOS32IPROTECTQUERYFHSTATE  ; call the internal query routine
        mov     di, DIsave
        mov     si, SIsave

        or      ax,ax                   ; error?
        jnz     short pdq9              ; yes, don't bother checking flags

        SaveReg <di,ds,es>
        les     di,pFlags               ; check for console handle in raw mode
        mov     al,byte ptr es:[di+2]
        and     al,(FHSTATE_CON or FHSTATE_RAWMODE) shr 16
        cmp     al,(FHSTATE_CON or FHSTATE_RAWMODE) shr 16
        jne     short pdq8              ; leave EOF bit alone (zero)

        mov     ax,data
        mov     ds,ax                   ; gain data segment addressability
        assume  ds:data

;  If there is buffered data, leave EOF bit clear

        mov     si,pNext                ; get pointer to next char
        cmp     si,pEnd                 ; is it = to end (no chars in buffer)
        jnz     pdq8                    ; no, go copy out data

;       We need to call KbdPeek to determine the value of the console EOF bit

        push    ds                      ; segment of char buffer
        push    offset KbdCharBuf       ; offset of char buffer
        push    0                       ; keyboard handle (unused/reserved)
        call    KBDPEEK                 ; go do operation               ;MDLL
        or      ax,ax                   ; was there an error
        jnz     short pdq8              ; yes, go return it

        test    [KbdCharBuf].char_status,KBDCHAR_FINAL
        jnz     short pdq8              ; character is available
        or      byte ptr es:[di+2],(FHSTATE_EOF shr 16)

pdq8:   sub     ax,ax                   ; return success
        RestoreReg  <es,ds,di>
        assume  ds:nothing
pdq9:
;       MMIOPH Performance Hook Removed
;       Maj/Min = 3092H, Data = [pCount], ax (bytes read, rc)

        LeaveProc
        ret     ?aframe
EndProc DOSPROTECTQUERYFHSTATE


        BREAK   <DOSERRCLASS - Classify Error Codes>


;**     DOSERRCLASS - Classify Error Codes
;
;       PUSH    code          ; error code for analysis
;       PUSH@   WORD class    ; class retval
;       PUSH@   WORD action   ; action retval
;       PUSH@   WORD locus    ; locus retval
;       CALL    DosErrClass
;
;         Error classes
;
;           1   OutRes          Out of Resources
;           2   TempSit         Temporary situation
;           3   Auth            authorization failed
;           4   Intrn           Internal error
;           5   HrdFail         device hard failure
;           6   SysFail         system failure
;           7   Apperr          probable application error
;           8   NotFnd          Item not located
;           9   BadFmt          bad format for call/data
;          10   Locked          Resource/Data locked
;          11   Media           Incorrect media
;          12   Already         resource/action already taken/done
;          13   Unk             Unclassified
;          14   Cant            Can't perform requested action
;          15   Time            Timeout
;
;         Default/Recommended Action
;
;           1  Retry           retry immediately
;           2  DlyRet          delay and retry
;           3  User            user input was incor. - get new values
;           4  Abort           abort application
;           5  Panic           term. oper'ns immediately, no cleanup
;           6  Ignore          Ignore error
;           7  IntRet          ask user to correct and retry
;
;         Error Locus
;           1  Unk             Unknown        3  Net           Network
;           2  Disk            DASD           4  SerDev        Device
;           5  Mem             Memory
;
;       ENTRY   Stack Frame as above
;       EXIT    (AX) = 0 if OK
;               (AX) != 0 if error
;       USES:   FLAGS


; Must define emit_error_table to make errtab.inc generate data

emit_error_table equ 0ffffh

ErrorTable      label   word
.xlist
include errtab.inc              ;Include table giving class, action and locus
.list
                                ; for each error.
ErrorTablelen = ($-ErrorTable)/SIZE ErrEntry

procedure   DOSERRCLASS,FAR

ArgVar      pLocus,DWORD
ArgVar      pAction,DWORD
ArgVar      pClass,DWORD
ArgVar      Ecode,WORD

        EnterProc
        pusha                           ; save them registers
        push    es
        mov     ax,Ecode

;***DT  preDOSERRCLASS - DosErrClass pre-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_PRE_DOSERRCLASS>,
;          TP    = .preDOSERRCLASS,
;          TYPE  = (PRE,API),
;          GROUP = FS,
;          DESC  = "(OS) DosErrClass Pre-Invocation",
;          REGS  = (AX),
;          FMT   = "Error Code = <FMT_WORD>"

        PUBLIC  preDOSERRCLASS
preDOSERRCLASS:                         ; RAS pre-invocation trace
        MMPHPreDOSErrClass              ; Performance Hook (1C06h)

        mov     cx,ErrorTablelen
        mov     si,OFFSET CS:ErrorTable

;       Search for error code

dec1:   cmp     ax,cs:ErrorCode.[si]
        jz      short dec3              ; got it
        add     si,SIZE ErrEntry
        loop    dec1                    ; try each one

;       We fell off the end of the table (Bad Ecode)

        sub     si, SIZE ErrEntry       ; move pointer back

;       (CS:SI) = address of record

dec3:   movzx   ax,BYTE PTR cs:ErrClass.[si]    ; (ax) = classification
        les     di,pClass
        mov     WORD PTR es:[di],ax
        movzx   bx,BYTE PTR cs:ErrAction.[si]   ; (bx) = action
        les     di,pAction
        mov     WORD PTR es:[di],bx
        movzx   cx,BYTE PTR cs:ErrLocus.[si]    ; (cx) = locus
        les     di,pLocus
        mov     WORD PTR es:[di],cx
        pop     es

;***DT  postDOSERRCLASS - DosErrClass post-invocation trace point
;
;    TRACE MINOR = <RAS_DC1_POST_DOSERRCLASS>,
;          TP    = .postDOSERRCLASS,
;          TYPE  = (POST,API),
;          GROUP = FS,
;          DESC  = "(OS) DosErrClass Post-Invocation",
;          REGS  = (AX,BX,CX),
;          FMT   = "Class = <FMT_WORD>  Action = <FMT_WORD>",
;          FMT   = "Locus = <FMT_WORD>"

        PUBLIC  postDOSERRCLASS
postDOSERRCLASS:                        ; RAS post-invocation trace
        MMPHPostDOSErrClass             ; Performance Hook (1C86h)

        popa
        sub     ax,ax
        LeaveProc
        ret     ?aframe

EndProc DOSERRCLASS


CODE   ENDS




CODE32_TUNE2  SEGMENT

?cstype = ?CS_32bit

        ASSUME  CS:FLAT, DS:FLAT, ES:FLAT, SS:FLAT

CpuMode 386


;***FS  Dos32Read
;
;       DOS32READ is used to transfer bytes from a file/device into the user's
;       address space.  In the case of reading from the console, we use
;       KBDSTRINGIN to read into a local buffer and then deblock it out from
;       there.
;
;       Entry:  DWORD   pcbRead
;               DWORD   cbBuffer
;               DWORD   pchBuffer
;               DWORD   ulHandle
;
;       EXIT:   If successful then
;                   EAX = 0
;                   number of bytes read is filled in
;               if failure then
;                   EAX = error code
;
;       USES:   EAX, EDX, ECX, FLAGS


procedure   DOS32READ,NEAR

ArgVar      h,DWORD
ArgVar      pData,DWORD
ArgVar      count,DWORD
ArgVar      pCount,DWORD

LocalVar    ulHFlags,DWORD

        EnterProc

        cld
        push    0                       ; fake file handle lockID dword
        lea     eax,ulHFlags
        push    eax
        push    pCount
        push    count
        push    pData
        push    h
        call    DOS32IREAD
        add     esp,24                  ;clean up the 6 args pushed above

        cmp     eax,error_VIOKBD_request
        jnz     d32r_leave
;
; Preserve all necessary registers
;
        SaveReg <ebx,esi,edi>
;
; See if there is buffered data. If not, fill the internal buffer
;
        mov     esi,pNext32             ;get pointer to next char
        mov     edi,pEnd32
        cmp     esi,edi                 ;is it = to end (no chars in buffer)
        jne     d32r_usercopy           ;no, go copy out data

        mov     edi,offset FLAT:KbdInputBuf
        mov     pNext32,edi             ;reset pointers
        mov     pEnd32,edi
;
; Based on the I/O mode bit for this console handle, do raw or cooked input
;
        test    byte ptr ulHFlags+2,FHSTATE_RAWMODE shr 16
        jz      short d32r_cookinput

        sub     ebx,ebx                 ;initialize the IOWAIT parm
        .errnz  IOWAIT
        mov     esi,INPSIZ

d32r_rawinput:
        push    offset FLAT:KbdCharBuf
        push    ebx                     ;wait for character to be typed
        push    0                       ;keyboard handle (reserved)
        call    KBD32CHARIN
        or      eax,eax
        jnz     d32r_errexit            ;error, get out

        test    KbdCharBuf.char_status,KBDCHAR_FINAL
        jz      short d32r_usercopy     ;no character available

        mov     ax,word ptr KbdCharBuf.char_code
        stosb                           ;save character in internal buffer
        or      al,al
        jz      short d32r10
        cmp     al,0E0h
        jne     short d32r20
        or      ah,ah
        jz      short d32r20
d32r10: mov     al,ah                   ;save extended byte as well
        stosb
d32r20: mov     bl,1                    ;go to NOWAIT mode now
        dec     esi
        jnz     short d32r_rawinput     ;see if there are any more
        jmp     short d32r_usercopy     ;we just ran out of space, so go copy
;
; Fill the buffer by calling KBDSTRINGIN.  If any error occurs, return it
; without changing internal data
;
d32r_cookinput:
        mov     KbdLenBuf.input_len,INPSIZ

        push    edi
        push    offset FLAT:KbdLenBuf
        push    IOWAIT                  ;wait for characters to be typed
        push    0                       ;keyboard handle (reserved)
        call    KBD32STRINGIN
        or      eax,eax
        jnz     short d32r_errexit      ;error, get out
;
; See if ctrl-z was entered. If it was the first character, we flush the
; buffer and set count to 0. If it was not the first character, we throw
; away anything else that was entered after the ctrl-z (including the ctrl-z)
;
        cmp     byte ptr [edi],26
        je      short d32r30
        movzx   eax,KbdLenBuf.receive_len
        add     edi,eax
        mov     word ptr [edi],0A0Dh;CR/LF
        add     edi,2                   ;EDI == new value for pEnd32

d32r30: mov     KbdLenBuf.receive_len,ax
;
; KBD32STRINGIN left the cursor at the left margin.  Echo a line feed.
;
        push    offset FLAT:LF          ;offset of line feed
        push    1                       ;count
        push    0                       ;VIO handle (reserved)
        call    VIO32WRTTTY
;
; Copy data from buffer into user's address space.  EDI == new value for pEnd32
;
d32r_usercopy:
        mov     pEnd32,edi              ;update pEnd32
        mov     ecx,edi
        mov     esi,pNext32
        sub     ecx,esi                 ;ECX == # of bytes available to copy
        mov     edi,pData

        cmp     ecx,count               ;don't exceed supplied count limit
        jb      short d32r40
        mov     ecx,count

d32r40: mov     eax,ecx                 ;EAX == count to return
        rep     movsb                   ;copy the bytes
        mov     pNext32,esi             ;update the pNext pointer

        mov     esi,pCount              ;exit here to update count
        mov     [esi],eax               ;and return success
        xor     eax,eax

d32r_errexit:
        RestoreReg  <edi,esi,ebx>

d32r_leave:
        LeaveProc
        ret

EndProc DOS32READ

CODE32_TUNE2  ENDS




CODE32  SEGMENT

?cstype = ?CS_32bit

        ASSUME  CS:FLAT, DS:FLAT, ES:FLAT, SS:FLAT

CpuMode 386

;***FS  Dos32Write
;
;       DOSWRITE is used to transfer bytes to a file/device from the user's
;       address space.  In the case of writing to the console, we use
;       VIOWRTTTY to write to the screen
;
;       This routine is capable of handling very large (> 64k) writes
;
;       ENTRY:      DWORD   pcbWritten
;                   DWORD   cbBuffer
;                   DWORD   pchBuffer
;                   DWORD   ulHandle
;
;       EXIT:   If successful then
;                   EAX = 0
;                   number of bytes written is filled in
;               if failure then
;                   EAX = error code
;       USES:   FLAGS


        ASSUME  CS:FLAT, DS:FLAT, ES:FLAT, SS:FLAT

procedure   DOS32WRITE,NEAR

ArgVar  h,DWORD
ArgVar  pData,DWORD
ArgVar  count,DWORD
ArgVar  pCount,DWORD

LocalVar ulHFlags,DWORD
LocalVar ulWritten,DWORD                ;Record of bytes written so far

        EnterProc

        push    0                       ; fake file handle lockID
        lea     eax,ulHFlags
        push    eax
        push    pCount
        push    count
        push    pData
        push    h

        call    DOS32IWRITE             ;Try this handle
        add     esp,24                  ;clean up the 6 args pushed above

        cmp     ax,error_VIOKBD_request ;do we redirect to the VIO package?
        jnz     d32w90                  ;no, all done

        SaveReg <ebx,edi,esi>

        sub     eax,eax                 ;default to ANSI-disabled

;defect# 25355 remove call to VIO32SETANSI - starts
;#25355         test    byte ptr ulHFlags+2,FHSTATE_RAWMODE shr 16
;#25355         jnz     short d32w10            ;disabled it is (raw bit is on)
;#25355         inc     eax                     ;otherwise, enable ANSI
;#25355 d32w10: push    eax                     ;
;#25355         push    0                       ;VIO handle (reserved)
;#25355         call    VIO32SETANSI            ;set ANSI according to handle state
;#25355         or      eax,eax                 ;failed?
;#25355         jnz     d32wError
;defect# 25355 remove call to VIO32SETANSI - ends

        mov     ulWritten,eax           ;initialize total to zero

; - At this point, two things are possible
;
; - pBuf is 64k aligned and request is for >= 64k of data
;
;       This will require two writes for the segment, since the only way
;       to write a full 64k of data is in two peices. The largest chunk
;       is 64k-1 long.
;
; - pBuf is not 64k aligned
;
;       This case we can just write to the end of the segment
;
d32wMainLoop:
        mov     edx,pData
        or      dx,dx                   ;64k aligned?
        jnz     short d32w30            ;No, branch

        ;Now we know that we have 64k of room in this segment. We need
        ;to determine if we are about to write the whole thing

        mov     ecx,count
        cmp     ecx,0ffffh              ;Largest possible write request
        jbe     short d32w30            ;If below, then we can write it
                                        ;faster below

        ;We have now determined that the only way to do this buffer is
        ;in two parts. Now we make two requests. The first is 60k, and the
        ;second is 4k

        mov     ecx,0f000h              ;Request 60k
        call    d32wDoIo

        or      eax,eax
        jnz     short d32wExit

        mov     edx,pData
        mov     ecx,01000h              ;Request 1k
        call    d32wDoIo
        jmp     short d32wMainLoopEnd
;
; This section is used when the next request will be smaller than 64k.
;

d32w30: movzx   ecx,dx                  ;We know that DX has offset
        neg     cx                      ;This gives # bytes left in seg
        jz      short d32w40            ;If zero, then 64k aligned buffer
        cmp     ecx,count               ;MIN(bytes in seg,count)
        jle     short d32w50
d32w40: mov     ecx,count
d32w50: call    d32wDoIo                ;Output

d32wMainLoopEnd:
        or      eax,eax                 ;Exit if error
        jnz     short d32wExit
        mov     eax,count               ;Check to see if count is zero
        or      eax,eax
        jnz     d32wMainLoop            ;if it is, the zero is the exit code
;
; Final exit location
;
d32wExit:
        mov     ebx,ulWritten           ;Store count written into users buffer
        mov     edi,pCount
        mov     [edi],ebx

d32wError:
        RestoreReg  <esi,edi,ebx>

d32w90: LeaveProc
        RETP

;
; Local procedure d32wDoIo
;
;       pData holds address of buffer
;       ecx holds bytes to write
;
d32wDoIo:
        add     pData,ecx               ;Increment pointer
        sub     count,ecx               ;Reduce count to be written

        push    ecx                     ;save count

        push    edx                     ;offset of data
        push    ecx                     ;count
        push    0                       ;VIO handle (reserved)
        call    VIO32WRTTTY

        pop     ecx                     ;recover count

        or      eax,eax                 ;check for error
        jnz     short d32w100
        add     ulWritten,ecx           ;no error, update total
d32w100:
        retn                            ;Near return within procedure

EndProc DOS32WRITE


;***FS  Dos32ProtectRead
;
;       DOS32PROTECTREAD is used to transfer bytes from a file/device into the
;       user's address space.  In the case of reading from the console, we use
;       KBDSTRINGIN to read into a local buffer and then deblock it out from
;       there.
;
;       Entry:  DWORD   ulFHandleLockID
;               DWORD   pcbRead
;               DWORD   cbBuffer
;               DWORD   pchBuffer
;               DWORD   ulHandle
;
;       EXIT:   If successful then
;                   EAX = 0
;                   number of bytes read is filled in
;               if failure then
;                   EAX = error code
;
;       USES:   EAX, EDX, ECX, FLAGS


procedure   DOS32PROTECTREAD,NEAR

ArgVar      h,DWORD
ArgVar      pData,DWORD
ArgVar      count,DWORD
ArgVar      pCount,DWORD
ArgVar      FHandleLockID,DWORD

LocalVar    ulHFlags,DWORD

        EnterProc

        cld
        push    FHandleLockID                   ; file handle lockID
        lea     eax,ulHFlags
        push    eax
        push    pCount
        push    count
        push    pData
        push    h
        call    DOS32IPROTECTREAD
        add     esp,24                  ;clean up the 6 args pushed above

        cmp     eax,error_VIOKBD_request
        jnz     pd32r_leave
;
; Preserve all necessary registers
;
        SaveReg <ebx,esi,edi>
;
; See if there is buffered data. If not, fill the internal buffer
;
        mov     esi,pNext32             ;get pointer to next char
        mov     edi,pEnd32
        cmp     esi,edi                 ;is it = to end (no chars in buffer)
        jne     pd32r_usercopy          ;no, go copy out data

        mov     edi,offset FLAT:KbdInputBuf
        mov     pNext32,edi             ;reset pointers
        mov     pEnd32,edi
;
; Based on the I/O mode bit for this console handle, do raw or cooked input
;
        test    byte ptr ulHFlags+2,FHSTATE_RAWMODE shr 16
        jz      short pd32r_cookinput

        sub     ebx,ebx                 ;initialize the IOWAIT parm
        .errnz  IOWAIT
        mov     esi,INPSIZ

pd32r_rawinput:
        push    offset FLAT:KbdCharBuf
        push    ebx                     ;wait for character to be typed
        push    0                       ;keyboard handle (reserved)
        call    KBD32CHARIN
        or      eax,eax
        jnz     pd32r_errexit           ;error, get out

        test    KbdCharBuf.char_status,KBDCHAR_FINAL
        jz      short pd32r_usercopy    ;no character available

        mov     ax,word ptr KbdCharBuf.char_code
        stosb                           ;save character in internal buffer
        or      al,al
        jz      short pd32r10
        cmp     al,0E0h
        jne     short pd32r20
        or      ah,ah
        jz      short pd32r20
pd32r10: mov     al,ah                   ;save extended byte as well
        stosb
pd32r20: mov     bl,1                    ;go to NOWAIT mode now
        dec     esi
        jnz     short pd32r_rawinput     ;see if there are any more
        jmp     short pd32r_usercopy     ;we just ran out of space, so go copy
;
; Fill the buffer by calling KBDSTRINGIN.  If any error occurs, return it
; without changing internal data
;
pd32r_cookinput:
        mov     KbdLenBuf.input_len,INPSIZ

        push    edi
        push    offset FLAT:KbdLenBuf
        push    IOWAIT                  ;wait for characters to be typed
        push    0                       ;keyboard handle (reserved)
        call    KBD32STRINGIN
        or      eax,eax
        jnz     short pd32r_errexit      ;error, get out
;
; See if ctrl-z was entered. If it was the first character, we flush the
; buffer and set count to 0. If it was not the first character, we throw
; away anything else that was entered after the ctrl-z (including the ctrl-z)
;
        cmp     byte ptr [edi],26
        je      short pd32r30
        movzx   eax,KbdLenBuf.receive_len
        add     edi,eax
        mov     word ptr [edi],0A0Dh;CR/LF
        add     edi,2                   ;EDI == new value for pEnd32

pd32r30: mov     KbdLenBuf.receive_len,ax
;
; KBD32STRINGIN left the cursor at the left margin.  Echo a line feed.
;
        push    offset FLAT:LF          ;offset of line feed
        push    1                       ;count
        push    0                       ;VIO handle (reserved)
        call    VIO32WRTTTY
;
; Copy data from buffer into user's address space.  EDI == new value for pEnd32
;
pd32r_usercopy:
        mov     pEnd32,edi              ;update pEnd32
        mov     ecx,edi
        mov     esi,pNext32
        sub     ecx,esi                 ;ECX == # of bytes available to copy
        mov     edi,pData

        cmp     ecx,count               ;don't exceed supplied count limit
        jb      short pd32r40
        mov     ecx,count

pd32r40: mov     eax,ecx                ;EAX == count to return
        rep     movsb                   ;copy the bytes
        mov     pNext32,esi             ;update the pNext pointer

        mov     esi,pCount              ;exit here to update count
        mov     [esi],eax               ;and return success
        xor     eax,eax

pd32r_errexit:
        RestoreReg  <edi,esi,ebx>

pd32r_leave:
        LeaveProc
        ret

EndProc DOS32PROTECTREAD


;***FS  Dos32ProtectWrite
;
;       DOSPROTECTWRITE is used to transfer bytes to a file/device from the
;       user's address space.  In the case of writing to the console, we use
;       VIOWRTTTY to write to the screen
;
;       This routine is capable of handling very large (> 64k) writes
;
;       ENTRY:      DWORD   ulFileHandleLockID
;                   DWORD   pcbWritten
;                   DWORD   cbBuffer
;                   DWORD   pchBuffer
;                   DWORD   ulHandle
;
;       EXIT:   If successful then
;                   EAX = 0
;                   number of bytes written is filled in
;               if failure then
;                   EAX = error code
;       USES:   FLAGS


        ASSUME  CS:FLAT, DS:FLAT, ES:FLAT, SS:FLAT

procedure   DOS32PROTECTWRITE,NEAR

ArgVar  h,DWORD
ArgVar  pData,DWORD
ArgVar  count,DWORD
ArgVar  pCount,DWORD
ArgVar  FileHandleLockID,DWORD

LocalVar ulHFlags,DWORD
LocalVar ulWritten,DWORD                ;Record of bytes written so far

        EnterProc

        push    FHandleLockID                  ; file handle lockID
        lea     eax,ulHFlags
        push    eax
        push    pCount
        push    count
        push    pData
        push    h

        call    DOS32IPROTECTWRITE      ;Try this handle
        add     esp,24                  ;clean up the 6 args pushed above

        cmp     ax,error_VIOKBD_request ;do we redirect to the VIO package?
        jnz     pd32w90                 ;no, all done

        SaveReg <ebx,edi,esi>

        sub     eax,eax                 ;default to ANSI-disabled

        mov     ulWritten,eax           ;initialize total to zero

; - At this point, two things are possible
;
; - pBuf is 64k aligned and request is for >= 64k of data
;
;       This will require two writes for the segment, since the only way
;       to write a full 64k of data is in two peices. The largest chunk
;       is 64k-1 long.
;
; - pBuf is not 64k aligned
;
;       This case we can just write to the end of the segment
;
pd32wMainLoop:
        mov     edx,pData
        or      dx,dx                   ;64k aligned?
        jnz     short pd32w30           ;No, branch

        ;Now we know that we have 64k of room in this segment. We need
        ;to determine if we are about to write the whole thing

        mov     ecx,count
        cmp     ecx,0ffffh              ;Largest possible write request
        jbe     short pd32w30           ;If below, then we can write it
                                        ;faster below

        ;We have now determined that the only way to do this buffer is
        ;in two parts. Now we make two requests. The first is 60k, and the
        ;second is 4k

        mov     ecx,0f000h              ;Request 60k
        call    pd32wDoIo

        or      eax,eax
        jnz     short pd32wExit

        mov     edx,pData
        mov     ecx,01000h              ;Request 1k
        call    pd32wDoIo
        jmp     short pd32wMainLoopEnd
;
; This section is used when the next request will be smaller than 64k.
;

pd32w30: movzx   ecx,dx                 ;We know that DX has offset
        neg     cx                      ;This gives # bytes left in seg
        jz      short pd32w40           ;If zero, then 64k aligned buffer
        cmp     ecx,count               ;MIN(bytes in seg,count)
        jle     short pd32w50
pd32w40: mov     ecx,count
pd32w50: call    pd32wDoIo                ;Output

pd32wMainLoopEnd:
        or      eax,eax                 ;Exit if error
        jnz     short pd32wExit
        mov     eax,count               ;Check to see if count is zero
        or      eax,eax
        jnz     pd32wMainLoop           ;if it is, the zero is the exit code
;
; Final exit location
;
pd32wExit:
        mov     ebx,ulWritten           ;Store count written into users buffer
        mov     edi,pCount
        mov     [edi],ebx

pd32wError:
        RestoreReg  <esi,edi,ebx>

pd32w90: LeaveProc
        RETP

;
; Local procedure pd32wDoIo
;
;       pData holds address of buffer
;       ecx holds bytes to write
;
pd32wDoIo:
        add     pData,ecx               ;Increment pointer
        sub     count,ecx               ;Reduce count to be written

        push    ecx                     ;save count

        push    edx                     ;offset of data
        push    ecx                     ;count
        push    0                       ;VIO handle (reserved)
        call    VIO32WRTTTY

        pop     ecx                     ;recover count

        or      eax,eax                 ;check for error
        jnz     short pd32w100
        add     ulWritten,ecx           ;no error, update total
pd32w100:
        retn                            ;Near return within procedure

EndProc DOS32PROTECTWRITE

CODE32  ENDS


        END
