;       SCCSID = @(#)bksutil.asm	6.1 90/11/16
;       SCCSID = @(#)bksutil.asm	6.6 90/02/14
; ****************************************************************************
; *                                                                          *
; *                       IBM/Microsoft Confidential                         *
; *                                                                          *
; *                 Copyright (c) IBM Corporation  1987, 1990                *
; *                 Copyright (c) Microsoft Corp.  1987, 1990                *
; *                           All Rights Reserved                            *
; *                                                                          *
; ****************************************************************************
        PAGE    60,132
        TITLE   BKSUTIL.ASM - Keyboard String Input Routines

;******************************************************************************
;*******   C H A N G E       H I S T O R Y   **********************************
;******************************************************************************
;DCR357 03-23-89  THG   Ferrari Extended iff E0H charcode AND status bit 1 set
;@T52   01-15-90  TPL   DCR 704 work

.286p                           ; 286 Protect Mode instructions

.xlist
INCLUDE error.inc               ; DOS error file
INCLUDE error2.inc              ; Extended error code file
INCLUDE struc.inc               ; Structured Macros
INCLUDE bksxxx.inc              ; Equates and structure template file
INCLUDE bksextrn.inc            ; External data declaration file
.list

CSEG    SEGMENT BYTE PUBLIC 'CODE'
CSEG    ENDS

GDSEG   SEGMENT BYTE PUBLIC 'FAR_DATA'                                  ;@T52
GDSEG   ENDS

IDSEG   SEGMENT BYTE PUBLIC 'DATA'
IDSEG   ENDS


        EXTRN   KBDCHARIN:FAR           ; Read a character from keyboard
        EXTRN   ANSIKEYDEF:FAR          ; Get Key Reassignment
        EXTRN   DOSQHANDTYPE:FAR        ; Query handle type
        EXTRN   DOSGETDBCSEV:FAR        ; Get DBCS environment vector
        EXTRN   KBDGETCP:FAR            ; Get in-use codepage ID

        PUBLIC  ChkDBCS
        PUBLIC  KeyDefChk
        PUBLIC  RawFilBuf
        PUBLIC  ReadChar
        PUBLIC  GetKeyDef
        PUBLIC  ValidateInHand
        PUBLIC  GetDBCS_String



CSEG    SEGMENT BYTE    PUBLIC  'CODE'
        ASSUME  CS:CSEG,DS:NOTHING,ES:NOTHING

;************************ START OF SPECIFICATIONS ***********************
;*                                                                      *
;*  SUBROUTINE NAME:    Common Subroutines                              *
;*                                                                      *
;*  DESCRIPTIVE NAME:   Common Subroutines                              *
;*                                                                      *
;*  FUNCTION:   Common routines used elsewhere in BKS                   *
;*                                                                      *
;*  NOTES:      Executes on level 3 - Protect Mode ONLY                 *
;*                                                                      *
;*  ENTRY POINT:    ChkDBCS        - Check for DBCS character in range  *
;*                  KeyDefChk      - Read Key Definition from ANSI      *
;*                  RawFilBuf      - Raw mode character read            *
;*                  ReadChar       - Cooked mode character read         *
;*                  GetKeyDef      - Get 1 char from key definition     *
;*                  ValidateInHand - Validate input handle              *
;*                  GetDBCS_String - Get DBCS environment vector        *
;*                                                                      *
;*      LINKAGE:        call near                                       *
;*                                                                      *
;*  INPUT:              See individual routine                          *
;*                                                                      *
;*  EXIT NORMAL:        See individual routine                          *
;*                                                                      *
;*  EXIT ERROR:         See individual routine                          *
;*                                                                      *
;*  INTERNAL REFERENCES:  IDSEG  - Per process data area                *
;*           ROUTINES:    none                                          *
;*                                                                      *
;*  EXTERNAL REFERENCES:  none                                          *
;*           ROUTINES:    KBDCHARIN    - Read character record          *
;*                        ANSIKEYDEF   - Get ANSI key definitaion       *
;*                        DOSGETDBCSEV - Get DBCS Environment vector    *
;*                        DOSQHANDTYPE - Query handle type              *
;*                        KBDGETCP     - Get in-use codepage ID         *
;*                                                                      *
;************************* END OF SPECIFICATIONS ************************



ChkDBCS proc near
   ASSUME  DS:IDSEG

;********** DBCS Enabling **************
;********** DBCS Enabling  The following routine ChkDBCS will check
;********** DBCS Enabling  the contents of AL register.
;********** DBCS Enabling
;********** DBCS Enabling   If (AL) is within the range of the 1st
;********** DBCS Enabling   byte of the DBCS character, SET CARRY.
;********** DBCS Enabling
;********** DBCS Enabling   If (AL) is not within the range of the
;********** DBCS Enabling   1st byte of the DBCS character, NOT
;********** DBCS Enabling   SET CARRY.
;********** DBCS Enabling **************;

   push dx
   push bx
   push cx
   clc
   mov  cx,HALF_LCRANGE         ;process 2 byte at a time
   mov  bx,OFFSET CRange        ;BX = index reg

 L0310:
   mov  dx,ds:[bx]              ;dx = (under limit):(over limit)

   .if <nonzero dx>             ;if dx = 0 (not DBCS), then exit

     .if <dl BE al> AND         ;SET Carry flag if
     .if <al BE dh>             ;(DL <= AL <= DH)
       stc
     .else
       inc  bx          ;Next range check
       inc  bx          ;
       loop L0310
     .endif

   .endif

   pop  cx
   pop  bx
   pop  dx
   ret
ChkDBCS ENDP



KeyDefChk proc near
   ASSUME DS:IDSEG

   ;****************************************************************
   ;*  Character read from the keyboard is passed to ANSI if ANSI  *
   ;*  is enable.  If the character read has been redefined, then  *
   ;*  the key redefinition string will be processed first before  *
   ;*  going to the keyboard for any keystroke.  If ANSI is dis-   *
   ;*  able, then keystrokes will be read from the keyboard.       *
   ;*                                                              *
   ;*  87/09/11   Enhanced to allow ANSI to treat extended Ferrari *
   ;*             key as unique key.  ASCII & Scan Code read from  *
   ;*             the keyboard device driver will be passed to     *
   ;*             ANSI as is.  It is up to ANSI to determine if    *
   ;*             the key stroke read has been redefined.          *
   ;*                                                              *
   ;****************************************************************

   .if <ANSI_State EQ ON>       ; ANSI ON?
     mov  ax,Buf_End

     .if <Buf_Position EQ ax>   ; KeyDefBuf empty?
       call ReadChar            ; Read a character from keyboard

       .if <zero ax>
         mov  al,Char_Code      ; Setup parameter block for ANSI keydef
         mov  ah,Scan_Code
         mov  Buf_Length,128
         mov  KeyDefBufS,ds             ; KeyDefBuf selector
         mov  KeyDefBufO,OFFSET KeyDefBuf
         mov  KeyDefChar,ax             ; Character information
         push ds                        ; Pass @ of parameter block to ANSI
         push OFFSET KeyDefBufO
         call ANSIKEYDEF

         .if <zero ax>

           .if <Buf_Length A al>        ; Key def. exists (non-zero)?
             mov  Buf_Position,OFFSET KeyDefBuf
             mov  al,Buf_Length
             add  Buf_End,ax
             call GetKeyDef             ; Get character from KeyDefBuf
           .else                        ;

             .if <Char_Code EQ 0E0H> AND; StringIn treats Ferrari extended
             test Status,0002H          ;   E0 char code AND             ;DCR357
             .IF nz AND                 ;   status bit 1 set => ferrari  ;DCR357
             .if <Scan_Code A al>       ;   keys as normal extended key
               mov  Char_Code,al        ;
             .endif                     ;

           .endif

         .endif

       .endif

     .else
       call GetKeyDef           ; Get character from KeyDefBuf
     .endif

   .else                        ; ANSI OFF!
     call ReadChar              ; Read character from keyboard

     .if <zero ax> AND
     .if <Char_Code EQ 0E0H> AND        ; StringIn treats Ferrari extended
     test Status,0002H                  ;   E0 char code AND             ;DCR357
     .IF nz AND                         ;   status bit 1 set => ferrari  ;DCR357
     .if <Scan_Code A al>               ;   keys as normal extended key
       mov  Char_Code,al                ;
     .endif                             ;

   .endif

   ret
KeyDefChk endp



ReadChar proc near
   ASSUME  DS:IDSEG
   ;
   ;****************************************************************
   ;*  Common routine to read a character from the keyboard        *
   ;*  device driver.                                              *
   ;*                                                              *
   ;*  87/09/11    Strip out Ferrari key check, since it is being  *
   ;*              done at KeyDefChk.                              *
   ;****************************************************************
   ;
   push ds                      ; Setup parameters to do a read
   push OFFSET Char_Code
   push WAIT_
   push Proc_Hand
   call KBDCHARIN

   .if <zero ax>
     mov  al,Status             ; Get interim status
     and  al,INTERIM_CHAR_MASK  ; Mask off unwanted bits
     cmp  al,INTERIM_CHAR       ; Is this the Interim character ?
     je   ReadChar              ; YES !! ignore it and read next byte
     sub  al,al                 ; Make sure return code is cleared
   .endif

   ret
ReadChar endp



GetKeyDef proc near
   ASSUME  DS:IDSEG
   ;
   ;****************************************************************
   ;*  Get a character from the KeyDefBuf and place it into Char_  *
   ;*  Code or Char_Code & Scan_Code if current character is an    *
   ;*  extended ASCII.                                             *
   ;****************************************************************
   ;
   push si                      ; Save worker register
   mov  si,Buf_Position         ; Setup character pointer
   lodsb                        ; Get character from KeyDefBuf

   .if <al EQ 0>  OR            ; Normal extended ASCII?
   .if <al EQ 0E0H> AND         ; Ferrari extended ASCII?
   test byte ptr[si+2],0002H    ;  E0 char code AND                   ;DCR357
   .IF nz                       ;  status bit 1 set => ferr. ext.     ;DCR357

     .if <si B Buf_End>         ; Reached end of buffer?
       mov  ah,BYTE PTR ds:[si] ; Get next byte buffer

       .if <ah A 0>             ; Valid extended ASCII?
         inc  si                ; Update pointer
         mov  Scan_Code,ah      ;
         mov  al,ZERO           ;
       .endif

     .endif

   .endif

   mov  Char_Code,al            ;
   sub  ax,ax                   ; Assume error free

   .if <si EQ Buf_End>          ; End of KeyDef string?
     mov  si,OFFSET KeyDefBuf   ; Clear all
     mov  Buf_End,si            ;
     mov  Buf_Length,al         ;
   .endif

   mov  Buf_Position,si         ; Update current character pointer
   pop  si                      ; Restore worker register
   ret
GetKeyDef endp




ValidateInHand proc near
   ASSUME DS:IDSEG

   ;************************************************************************
   ;*  On entry:  DS = IDSEG   AX = 0   BX = [bp].KBD_Handle               *
   ;*             SI = [bp].FunctionNum DX = KBD_SG                        *
   ;************************************************************************

   .if <nonzero bx>             ;Ignore handle validation if Bond

     .if <bx NE Bond_Handle>            ;Handle or "Handle 0" passed!!!
       push bx                          ;Setup handle parm
       push ds                          ;Setup @ of handle type parm
       push OFFSET KeyDefChar           ;  1 = device
       push ds                          ;Setup @ of device attribute parm
       push OFFSET Transfer_Count       ;  01H = KBD input device
       call DOSQHANDTYPE                ;

       .if <zero ax>  AND               ; NO error encountered and
       .if <KeyDefChar EQ 1>  AND       ;   Handle Type = DEVICE and
         test Transfer_Count,1          ;   Handle Attribute = KBD input device
       .if <nz>                         ;

         .if <[bp].FunctionNum NE FC_CLOSE> AND ; Allow KBDGETFOCUS
         .if <[bp].FunctionNum NE FC_GETFOCUS>  ; and KBDCLOSE to pass through
           mov ax,ERROR_KBD_FOCUS_REQUIRED
         .endif

       .else                            ; Non device handle specified
         mov  ax,ERROR_KBD_INVALID_HANDLE
       .endif

     .else                      ; Allow KBDCLOSE or KBDGETFOCUS on
       test Flag,FOCUS_ON       ;   a handle that is freed!

       .if <z> AND
       .if <[bp].FunctionNum NE FC_CLOSE> AND
       .if <[bp].FunctionNum NE FC_GETFOCUS>
         mov  ax,ERROR_KBD_FOCUS_REQUIRED
       .endif

     .endif

   .endif

   ret
ValidateInHand endp



RawFilBuf PROC  NEAR
   ASSUME  DS:IDSEG

   ;****************************************************************
   ;*  RAW Mode String Processing - CX contains character count    *
   ;****************************************************************

ReadAgain:
   push  ds                     ; Set up CharData buffer address
   push  OFFSET Char_Code
   push  [bp].IoWait            ; Wait/No Wait parameter
   push  [bp].KBD_Handle        ; Dummy device handle
   call  KBDCHARIN              ; Read a single character

   .if <zero ax>
     and    Transfer_Count,01H  ; Strip unnecessary bits

     .if <nz>                   ; Character available?
       test  Status,ON          ; Shift report ONLY?
       jnz   ReadAgain          ; Shift state is not returned!!!

       ;*** DBCS Enabling **************
       ;*** DBCS Enabling If it is interim character, then ignore it.
       ;*** DBCS Enabling **************;
       and  Status,INTERIM_CHAR_MASK ;

       .if <Status NE INTERIM_CHAR> near
         mov   al,Char_Code

         .if <dh B dl>          ; End of buffer reached?
           inc  dh              ; Bump byte count
           stosb                ; Stuff Char code

           .if <al EQ EXTENDED> OR      ; Extended ASCII (AT or Fararri)?
           .if <al EQ 0E0H> AND
           test Status,0002H            ;  E0 char code AND              ;DCR357
           .IF nz AND                   ;  status bit 1 set => ferrari   ;DCR357
           .if <Scan_Code A ZERO>

             .if <dh EQ dl>             ; End of buffer reached?
               inc cl                   ; Raw mode-wait terminator
               dec Transfer_Count       ; Raw mode-nowait terminator
             .else                      ; Can accommodate Scan code!
               inc dh                   ; Bump byte count
               mov al,Scan_Code         ; Stuff Scan code
               stosb
             .endif

             xor  ax,ax

             ;*** DBCS Enabling **************
             ;*** DBCS Enabling  Do the DBCS operation to avoid the
             ;*** DBCS Enabling  Buffer Full.
             ;*** DBCS Enabling **************;
           .else
             call ChkDBCS       ; Check for DBCS character
             mov  ax,0          ; Clear AX but DON'T CLEAR CARRY

             .if C              ; If it is the DBCS double-byte char,

               .if <dh EQ dl>
                 ;
                 ;Buffer is full...
                 ;  Discard  the 2nd byte of the DBCS
                 ;  double byte character from the KIB.
                 ;
                 push ds
                 push OFFSET Char_Code
                 push ax
                 push ax
                 call KBDCHARIN

                 mov  Sentinel,ovrflow
                 mov  Transfer_Count,ax
                 dec  dh        ; recover the input buffer counter
                 dec  di        ; recover the input buffer pointer
               .endif

             .endif

           .endif

         .endif

       .endif

     .endif

   .endif

   ret
RawFilBuf endp



GetDBCS_String proc near
   ASSUME  DS:IDSEG,ES:GDSEG

   ;************************************************************************
   ;*  On entry:  DS = IDSEG   AX = 0                                      *
   ;************************************************************************

   push ax                      ; @@ Setup: DWORD of zero
   push ax                      ; @@
   push ds                      ; @@        Address of codepage ID
   push OFFSET CP_ID            ; @@          returned
   push [bp].KBD_Handle         ; @@        caller's handle
   call KBDGETCP                ; @@ Get in-use codepage ID

   .if <zero ax>
     push LCRANGE               ; @  Setup: Length field
     push ds                    ; @         Address of Country Code
     push OFFSET CCode          ; @
     push ds                    ; @         Address of DBCS code range
     push OFFSET CRange         ; @
     call DOSGETDBCSEV          ; @  Get DBCS Environment vector
     xor  ax,ax                 ; @@ Clear error
   .endif

   ret
GetDBCS_String endp



CSEG ends
     end
