;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
    Page  58,132
    Title KBDVDM - VDM IDC Entry Point Router/Handler

;/*************************************************************************
;*
;* SOURCE FILE NAME = KBDVDM.ASM
;*
;* DESCRIPTIVE NAME = Physical Keyboard Device Driver IDC Entry Point
;*
;*
;* VERSION      V2.0
;*
;* DATE         08/12/91
;*
;* DESCRIPTION  This file communicates with the Virtual Keyboard Device
;*              Driver. The VKBD sets up the stack data for a specific
;*              function. This file filters the function to the appropriate
;*              worker routine to service the call.
;*
;*
;* FUNCTIONS    NONE
;*
;*
;* NOTES        DEPENDENCIES:  The VKBD must follow the calling conventions that
;*                             have been spec'ed out.
;*              RESTRICTIONS:  Machine must be a 386 or compatible with a 386.
;*
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   8/11/91               DCR 1195 Added support for this function
;*
;*
;*
;*
;*
;**************************************************************************



.286p                        ;* Enables assembly of 80386 protected instr
.sall                        ;* Suppresses listing of all macro expansions
.xcref                       ;* Suppresses the listing of symbols in the CR
.xlist                       ;* Suppresses the listing of the following
incl_mi equ 1                ;*
  include     mvdm.inc       ;* Equates used by vkbdpdd.h
  Include     osmaca.inc     
  CPUMODE     286            ;*  Macro file for OS/2 kernel
  include     vkbdpdd.inc    ;* Common structures and data shared between V/P(KBD)
  include     devsym.inc     ;* Various device driver equates
  Include     kbdseg.inc     ;* Segment definitions.
  include     struc.inc      ;* Structured macros
  include     kbdddr.inc     ;* Various KBD equates
  include     devhlp.inc     ;* DevHlp equates for DD services
  include     kbdxlat.inc    ;* codepage table format equates
.list                        ;* Restores the listing

;** External Routines

  Extrn       AccessKCB        :Near
  Extrn       SendVDDSC        :Near
  Extrn       VDMPreXlate      :Near
  Extrn       DD_Entry         :Far
  Extrn       SGActivation     :Near

;** External Variables

  Extrn       VDMKeyQ        :byte
  Extrn       VDMQInPtr      :byte
  Extrn       CmdFlags       :byte
  Extrn       VDMFocus       :byte
  Extrn       PSGPointers    :word
  Extrn       KbdHWIDs       :word
  Extrn       CPCount        :word
  Extrn       PDDtoVDDSCPkt  :word
  Extrn       CBDataSel      :word
  Extrn       VDDEntryPoint  :fword
  Extrn       HKBypass       :Dword         
  Extrn       SQHotKeyData   :Byte          

Code    SEGMENT

   ;*
   ;* When the PKBD entry point (PDDCMD_Entry) is called by the VKBD our data
   ;* selector is not set up. We must store it in our code segment at init time
   ;* so we could gain access to it when we are called by the VKBD.
   ;*

        Public selKbdData
selKbdData      dw      ?


even                                        ;* Put call table on word boundary.
PDDVDDTbl label   word        ;* Case table for the PDD/VDD interface functions
    dw    offset KbdCode:VDMRegister        ;* 0
    dw    offset KbdCode:VDMDeRegister      ;* 1
    dw    offset KbdCode:VDMCreated         ;* 2
    dw    offset KbdCode:VDMQueryID         ;* 3
    dw    offset KbdCode:VDMQueryInhibit    ;* 4
    dw    offset KbdCode:VDMSetLEDs         ;* 5
    dw    offset KbdCode:VDMSetRepeatRate   ;* 6
    dw    offset KbdCode:VDMTranslate       ;* 7
    dw    offset KbdCode:VDMTerminate       ;* 8
    dw    offset KbdCode:VDMFocusChange     ;* 9
    dw    offset KbdCode:VDMQueryRevXlate   ;* 10
    dw    offset KbdCode:VDMHotKeyControl   ;* 11
PDDMaxFunc  equ ($ - PDDVDDTbl)/2-1         ;* highest req # allowed


  Assume  cs:KbdCode,ds:DGroup,es:NOTHING,ss:NOTHING

;*********************************************************************
;*
;*  FUNCTION NAME   :  PDDCMD_Entry
;*
;*  DESCRIPTION     :  Keyboard Physical Device Driver IDC entry
;*                     point and request router routine.
;*
;*                     This routine is the Keyboard Physical DD
;*                     IDC entry point router/handler. IDC function
;*                     requests are routed to the appropriate worker
;*                     The address of this routine is setup via the
;*                     DevHelp RegisterPDD call during initialization.
;*
;*
;*  INPUT           :  On entry the stack is set-up as shown below:
;*
;*                     TOS --> return address (4 words)
;*                             variable 2     (2 words)
;*                             variable 1     (2 words)
;*                             function code (2 words)
;*
;*   Where: Var1 is a pointer to the input data structure and
;*          Var2 is a pointer to the output data structure for
;*          most of the commands.
;*
;*  RETURN-NORMAL     : Function performed, AX, ES, and DS registers
;*                    preserved, other registers changed as needed
;*                    by worker routines.
;*
;*  RETURN-ERROR      :  AX = 0, BX = error return code, DS and ES
;*                     preserved, other registers are undefined.
;*
;*  OUTPUT          :  NONE. (Registers modified.  See individual functions for
;*                     other possible effects).
;*
;*  INTERNAL REFERENCES:
;*     ROUTINES:
;*                    VDMRegister
;*                    VDMVDMCreated
;*                    VDMQueryID
;*                    VDMQueryInhibit
;*                    VDMSetLEDs
;*                    VDMSetRepeatRate
;*                    VDMTranslate
;*
;*  EXTERNAL REFERENCES:
;*     ROUTINES:  NONE.
;*     DevHelps:  NONE.
;*
;**************************************************************************

Procedure PDDCMD_Entry Far

  ?abase = 8 + 2                    ;* ret addr is 8 bytes, bp is 2 bytes
  ArgVar   Var2 DWORD
  ArgVar   Var1 DWORD
  ArgVar   Func DWORD

  StkFrameVars                      ;* Macro defining some global stack vars

  EnterProc
  SaveReg <SI,DI,DS,ES,BX>
  Mov   DS,CS:[selKbdData]
  Xor   AX,AX                               ;* assume failure
  Mov   BX,word ptr [Func]                  ;* (BX)=Func
  .If <BX be PDDMaxFunc>
     Shl   BX,1                             ;* adjust BX as a word index
     Mov   SI,word ptr [Var1]               ;* low word portion
     Mov   CX,word ptr [Var1+2]             ;* high word, (CX:si)=Var1
     Mov   DI,word ptr [Var2]               ;* low word portion
     Mov   DX,word ptr [Var2+2]             ;* high word, (dx:di)=Var2
     Call  CS:PDDVDDTbl[BX]                 ;* process requested function
  .Endif                                    ;* no error recovery yet
  RestoreReg <BX,ES,DS,DI,SI>
  LeaveProc
  Retfd ?aframe                             ;* tmp fix

EndProc PDDCMD_Entry




;***************************************************************************
;*
;*  FUNCTION NAME  :  VDMRegister        Function (0)
;*
;*  DESCRIPTION    :  Register KBD VDD entry point address.
;*
;*                    This routine recieves the VDD IDC Entry Point
;*                    Router address (VKPDDProc in VKBD) and stores
;*                    it in the PKBD data segment (VDDEntryPoint).
;*
;*
;*  INPUT         :   SI   - 16 bit Segment of the VDD Entry Point
;*                    DX:DI - 32 bit  Offset of the VDD Entry Point
;*
;*  OUTPUT        :   NONE.
;*
;*  RETURN-NORMAL   :  VDD Entry Point address saved.
;*
;*  RETURN-ERROR    :  AX = 0.
;*                   BX = ParmError if     structure.
;*
;*  EFFECTS       :  VDD Entry Point address saved to be used by
;*                   KbdInt.  All registers preserved except under
;*                   error condition.
;*
;*
;**************************************************************************

Public VDMRegister
VDMRegister Proc

  Mov word ptr [VDDEntryPoint],DI           ;* Get the VDD's offset (low word)
  Mov word ptr [VDDEntryPoint+2],DX         ;* Get the VDD's offset (high word)
  Mov word ptr [VDDEntryPoint+4],SI         ;* Get the VDD's EP selector
  Mov AX,TRUE                               ;* return success
  Ret

VDMRegister Endp



;***************************************************************************
;*
;*  FUNCTION NAME   :  VDMDeRegister        Function (1)
;*
;*  DESCRIPTION     :  DeRegister KBD VDD entry point address.
;*
;*                     Remove the VDD entry point which have been
;*                     registered. This is in response to
;*                     VDMDEREGISTER and is invoked by the VDD
;*                     during MVDM shut down.
;*
;*
;*  INPUT          :  None
;*
;*  OUTPUT         :  NONE.
;*
;*  RETURN-NORMAL    :  VDD Entry Point address saved.
;*                AX set to TRUE (-1)
;*
;*  RETURN-ERROR     :  AX = 0.
;*                    BX = ParmError if     structure.
;*
;*  EFFECTS        :  VDD Entry Point address saved to be used by
;*                    KbdInt.  All registers preserved except under
;*                    error condition.
;*
;*
;**************************************************************************

Public VDMDeRegister
VDMDeRegister Proc

  Xor AX,AX
  Mov word ptr [VDDEntryPoint],AX
  Mov word ptr [VDDEntryPoint+2],AX
  Mov word ptr [VDDEntryPoint+4],AX         ;* [VDDEntryPoint] = NULL
  Mov AX,TRUE                               ;* return success
  Ret

VDMDeRegister Endp


;***************************************************************************
;*
;*  FUNCTION NAME  :  VDMCreated       Function (2)
;*
;*  DESCRIPTION    :  VDM creation/activation notification.
;*
;*                    This routine receives an input address to a VDM
;*                    start information record located in the KBD VDD
;*                    data area. This routine is called by the KBD VDD
;*                    to indicate that this VDM is fully functional
;*                    for keyboard VDD keystroke scan code processing.
;*                    All type ahead scan codes are fed to the VDD
;*                    via it's IDC EventNotify function.
;*
;*
;*  INPUT         :  CX:SI points to the VKBD input data packet below
;*
;*                     ksig_len  dw  ; length of packet (4-bytes)
;*                     ksig_sgig dw  ; screen group ID
;*
;*  OUTPUT        :  NONE.
;*
;*  RETURN-NORMAL   : Typahead fed to KBD VDD. Interrupt time
;*                  processing enabled.
;*
;*  RETURN-ERROR    :  AX = 0.
;*                   BX = ParmErr if     structure length.
;*
;*  EFFECTS       :  Registers changed as needed.
;*
;*  INTERNAL REFERENCES:
;*     ROUTINES   :       SendVDDSC   SGActivation
;*
;*  EXTERNAL REFERENCES:
;*     ROUTINES   :       VDDCMD_KBDEvent
;*     DevHelps   :       NONE.
;*
;***************************************************************************

Public VDMCreated
VDMCreated Proc

  Mov ES,CX                                   ;* set es to data packet pointer
  .If <ES:[SI].ksgid_len eq KSGID_PKT_LEN>    ;* if length is correct
                                              ;*
      Mov   AX,ES:[SI].ksgid_sgid             ;* set up screen group ID (number).
      Mov   BL,1                              ;* Indicate to set Activation bit.
      Call  SGActivation                      
                                           ;* Go indicate that this VDM is active.
     .If <VDMQInPtr lt MAX_VDM_SC>            ;* If not last position in Q
       Mov   CL,MAX_VDM_SC                    ;* MAX scan codes buffered
       Sub   CL,VDMQInPtr                     ;* Actual amt of buffered SCs
       Lea   BX,VDMKeyQ                       ;* Get offset to Q
       Add   BX,MAX_VDM_SC                    ;* Adjust to the top of Q
       .While <NONZERO CL>                    ;* While there are SCs left in the Q
         Xor   AH,AH                          ;* Zero out high nibble
         Mov   AL,[BX]                        ;* Get scan code from Q
         Mov   PDDtoVDDSCPkt.kint_chScan,AX   ;* Put scan code in VDDs packet
         Mov   AX,Word Ptr ES:[SI].ksgid_sgid ;* Session group ID
         Mov   PDDtoVDDSCPkt.kint_usSGID,AX   ;* Put SGID code in VDDs packet
         Call  SendVDDSC                      ;* Send the scan code to the VDD
         Dec   BX                             ;* Adjust to next SC out
         Dec   CL                             ;* Decrement SC in buffer cntr
       .Endwhile                              ;*
       Mov   VDMQInPtr,MAX_VDM_SC             ;* Reset Q input ptr for next time
     .Endif                                   ;*
     Mov   AX,TRUE                            ;* return success
  .Else                                       ;* incorrect length specified
     Xor   AX,AX                              ;* clear AX to flag error
     Mov   BX,ParmError                       ;* return invalid paramater error
  .Endif
  Ret

VDMCreated Endp



;**************************************************************************
;*
;*  FUNCTION NAME : VDMQueryID       Function (3)
;*
;*  DESCRIPTION   : VDM get attached keyboard device ID.
;*
;*                  This function returns the physical keyboard
;*                  hardware device ID.
;*
;*
;*  INPUT         : DX:DI points to the VKBD output packet below
;*
;*                     kid_len DW  ; length of packet (4)
;*                     kid_ID  DW  ; keyboard ID
;*
;*
;*  OUTPUT        :  Keyboard device ID returned in VDD's data packet.
;*
;*  RETURN-NORMAL   :  Data record returned.
;*
;*  RETURN-ERROR    :  AX = 0, BX = error return code
;*
;*  EFFECTS       :  Same affects as Generic IOCtl category 4,
;*                   function 7Ah, GetKbdHWID.
;*
;***********************************************************************

Public VDMQueryID
VDMQueryID Proc

  Mov      ES,DX                              ;* Selector of VDD's data packet
  Mov      AX,KbdHWIDs                        ;* (AX) = keyboard hardware ID
  Mov      ES:[DI].kid_ID,AX                  ;* store in VDDs data packet
  Mov      ES:[DI].kid_len,KID_PKT_LEN        ;* return packet is 4 bytes long
  Mov      AX,TRUE                            ;* return success
  Ret

VDMQueryID Endp


;***************************************************************************
;*
;*  FUNCTION NAME  : VDMQueryInhibit      Function (4)
;*
;* DESCRIPTION     : VDM get keyboard controller inhibit flag.
;*
;*                   This function returns an output structure to the
;*                   Keyboard VDD which contains the current keyboard
;*                   controller's inhibit flag status. A byte is
;*                   returned where all bits are masked off except bit
;*                   0.
;*
;*
;*  INPUT          : DX:DI points to the VKBD output packet below
;*
;*                    kinh_len         DW ; length of packet (3)
;*                    kinh_fInhibited  DB ; bit 0  = 1 keyboard inhibited
;*                                        ; bit 0  = 0 keyboard not inhibited
;*                                        ; bit 1-7= reserved and set to 0
;*
;*
;*  OUTPUT         : ES:DI points to the KBD PDD data area which
;*                   contains the Inhibit data record.  Low order bit
;*                   of Flags byte will indicate whether the keyboard
;*                   is inhibited or not. All other flags will be set
;*                   to zero.
;*
;*  RETURN-NORMAL    : VDD's  ata record returned and device interrupts
;*
;*  RETURN-ERROR     :  AX = 0, BX = error return code
;*
;*  INTERNAL REFERENCES:
;*     ROUTINES    :
;*                         Invoke_ABIOS
;*
;*  EXTERNAL REFERENCES:
;*     ROUTINES    :  NONE.
;*     DevHelps    :       AllocReqPacket
;*                         FreeReqPacket
;*
;*************************************************************************

Public VDMQueryInhibit
VDMQueryInhibit Proc

  Add    DI,2                               ;* Adjust for inhibit byte address
  Push   DI                                 ;* Var2 (Callers offset)
  Push   DX                                 ;* Var1 (Callers selector)
  Push   DD_Cmd_QInhibit                    ;* Function (Query Inhibit)
  Call   DD_Entry                           ;* Device Independent Entry Point
  Mov    AX,TRUE                            ;* operation was successful
  Ret

VDMQueryInhibit Endp

;****************************************************************************
;*
;*  FUNCTION NAME : VDMSetLEDs       Function (5)
;*
;*  DESCRIPTION   : VDM set keyboard LEDs.
;*
;*                  This function takes the provided input data and
;*                  updates the physical keyboard device LEDs. This
;*                  data is range checked for validity and an INVALID
;*                  PARAMETER error code returned if the input values
;*                  are out of range. The range is determined by the
;*                  hardware mask bits 0 - 2 valid, all others are
;*                  reserved, = 0.
;*
;*
;*  INPUT        :  CX:SI points to the VKBD input packet below
;*
;*                    kled_len         DW ; length of packet (4)
;*                    kled_fsLEDs      DW ; bit 0 = SCROLL LOCK
;*                                        ; bit 1 = NUM LOCK
;*                                        ; bit 2 = CAPS LOCK
;*
;*  OUTPUT       :  NONE.
;*
;*  RETURN-NORMAL  :  Keyboard LEDs updated and device interrupts
;*                  are re-enabled. AX, ES, and DS preserved.
;*
;*  RETURN-ERROR   :  AX = 0, BX = ParmError return code or GenError
;*                  return code.
;*
;*  EFFECTS      :  Same affects as Generic IOCtl category 4,
;*                  function 5Ah, SetKbdLEDs.  All registers
;*                  preserved execpt AX, BX in error conditions.
;*
;*
;*  EXTERNAL REFERENCES:
;*     ROUTINES  :         NONE.
;*     DevHelps  :        AllocReqPacket
;*                        FreeReqPacket
;*
;*************************************************************************

Public VDMSetLEDs
VDMSetLEDs Proc

  Mov  StkTmpH,CX                           ;* Save VDD data packet selector
  Mov  ES,CX                                ;* Put VDD data selector in seg reg
  .If <ES:[SI].kled_len eq KLEDS_PKT_LEN>   ;* If packet is appropriate length
    Mov   AX,ES:[SI].kled_fsLEDs            ;* Get desired LED state
    Push  2222h                             ;* Var2 (Not used for this call)
    Push  AX                                ;* Var1 (LED state)
    Push  DD_Cmd_Indicators                 ;* Function
    Call  DD_Entry                          ;* Device Independent Entry Point
    Mov  AX,TRUE                            ;* return success
  .Else                                     ;* Incorrect input packet length
    Xor AX,AX                               ;* clear AX to flag error
    Mov BX,ParmError                        ;* return invalid paramater error
  .Endif
  Ret

VDMSetLEDs Endp




;**************************************************************************
;*
;*  FUNCTION NAME  : VDMSetRepeatRate      Function (6)
;*
;*  DESCRIPTION    : VDM set rate and delay.
;*
;*                   This function takes the provided input data and
;*                   updates the device hardware rate/delay values.
;*                   This data is not range checked for validity it is
;*                   assumed that the Keyboard VDD is sending proper
;*                   data. Also, a wrap function is performed by the
;*                   hardware which may be the intended action of the
;*                   VDM application in providing values out of range.
;*
;*  INPUT         :  CX:SI points to the VKBD input packet below
;*
;*                       krpt_len         DW ; length of packet (6)
;*                       krpt_usDelay     DW ; delay in ms. (0 implies default)
;*                       krpt_usRate      DW ; rate in cps. (0 implies default)
;*
;*  OUTPUT        : NONE.
;*
;*  RETURN-NORMAL   : Keyboard response rate updated, device ints
;*                  are re-enabled.
;*
;*  RETURN-ERROR    : AX = 0, BX = error return code, DS and ES
;*                  registers preserved.
;*
;*  EFFECTS       :  Same affects as Generic IOCtl category 4,
;*                   function 54h, SetKbdLEDs.  Registers preserved
;*                   except AX, BX on error condition.
;*
;*  INTERNAL REFERENCES:
;*     ROUTINES   :
;*
;*
;*  EXTERNAL REFERENCES:
;*     ROUTINES   :  NONE.
;*     DevHelps   :  AllocReqPacket
;*                   FreeReqPacket
;*
;****************************************************************************

Public VDMSetRepeatRate
VDMSetRepeatRate Proc

  Mov   ES,CX                               ;* Put VDD data selector in seg reg
  .If <ES:[SI].krpt_len eq KRPT_PKT_LEN>    ;* If packet is appropriate length
    Mov   BX,ES:[SI].krpt_usDelay           ;* Delay value in ms.
    Mov   AX,ES:[SI].krpt_usRate            ;* Repeat rate in cps.
    Mov   AH,BL                             ;* Put Delay value in high nibble
    Push  2222h                             ;* Var2 (Not used for this call)
    Push  AX                                ;* Var1 (Rate and Delay)
    Push  DD_Cmd_Typematic                  ;* Function
    Call  DD_Entry                          ;* Device Independent Entry Point
    Mov   AX,TRUE                           ;* return success
  .Else                                     ;* Incorrect input packet length
    Xor   AX,AX                             ;* clear AX to flag error
    Mov   BX,ParmError                      ;* return invalid paramater error
  .Endif
  Ret

VDMSetRepeatRate Endp


;***************************************************************************
;*
;*
;*  FUNCTION NAME : VDMTranslate     Function (7)
;*
;*  DESCRIPTION   : VDM translate scan code.
;*
;*                  This function takes the provided input data (scan
;*                  code field)  and translates it using the Key-
;*                  Packet structures and Translation flags fields,
;*                  into an ASCII character which is returned in the
;*                  CharDataRec fields of the input data area.
;*
;*
;*  INPUT         : CX:SI points the Translate record structure stored in
;*                  the VKBD's data area.
;*
;*  OUTPUT        : NONE. (imbedded in input record)
;*
;*  RETURN-NORMAL   : Input data record, output fields are updated.
;*
;*  RETURN-ERROR    : AX = 0, BX = ParmErr error code returned for
;*                  either caller's length invalid or mismatch of
;*                  requested codepage.  All registers preserved
;*                  except AX, BX under error conditions.
;*
;*  EFFECTS       :  Registers changed as needed.
;*
;*  INTERNAL REFERENCES:
;*     ROUTINES   :
;*
;*  EXTERNAL REFERENCES:
;*     ROUTINES   :  NONE.
;*     DevHelps   :  NONE.
;*
;**********************************************************************

Public VDMTranslate
VDMTranslate Proc

  Mov   StkDS,DS                            ;* Save our Data Selector
  Mov   StkTmpH,CX                          ;* Save VDD data packet selector
  Mov   ES,CX                               ;* Put VDD data selector in seg reg
  Mov   AX,ES:[SI].kxlt_usCodePage          ;* Obtain Code Page value from pkt
  Mov   ES,CBDataSel                        ;* Get the CPCB's selector
  Mov   DI,CPCBOFFSET                       ;* Offset to CPCB within selector
  Mov   CX,CPCount                          ;* Get # of entries in CPCB
  .If <bit CmdFlags nz CP_ENABLED>          ;* If code pages enabled
    Add   DI,CPCBEntrySize                  ;* Point to next CPCB entry
    Dec   CX                                ;* Reduce # of CP entries
  .Endif

     ;*
     ;* Right below I'm putting a temporary bandaid for a flaw in the VKBD.
     ;* He is calling us with a Code Page value of ZERO. We will use the
     ;* default code page if he passes us a ZERO. J.C.
     ;*

  .If <NONZERO AX>                          ;* If a code page value is specified
    .While <AX ne ES:[DI].CPValue>          ;* Loop thru the entries in the CPCB
      Dec   CX                              ;* Reduce # of CP entries to check
    .Leave <ZERO CX>                        ;* Exit if no more CPCB entries
      Add   DI,CPCBEntrySize                ;* Point to next CPCB entry
    .Endwhile                               ;* CP matched or no more entries
  .Else                                     ;* If VKBD passes a 0 CP value
    Mov   AX,ES:[DI].CPValue                ;* Use the default CP value
  .Endif

  .If <AX eq ES:[DI].CPValue>               ;* If we found a match
    Xor   BX,BX                             ;* Offset to TT will always be 0
    Mov   ES,word ptr ES:[DI].CPVAddr       ;* Get selector of Translate Table
    Mov   DS,StkTmpH                        ;* Get VDDs data packet selector
    Mov   DI,word ptr [SI].kxlt_f16pKey2    ;* Offset to KeyPacket2
    Mov   StkTmpL,DI                        ;* Save it on the local stack
    Mov   DI,word ptr [SI].kxlt_f16pKxf     ;* Offset to the xlation flags pkt
    Mov   SI,word ptr [SI].kxlt_f16pKey1    ;* Offset to KeyPacket1
    Mov   [SI].DDFlags,0                    ;* zero out DDFlags in KeyPacket1
    Mov   AL,[SI].Key.Scan                  ;* Scan code
    Call  VDMPreXlate                       ;* Pre-Xlation for VDM scan codes
    Mov   AX,TRUE                           ;* return success
  .Else
    Xor   AX,AX                             ;* clear AX to flag error
    Mov   BX,ParmError                      ;* return invalid paramater error
  .Endif
  Ret

VDMTranslate Endp


;***************************************************************************
;*
;*  FUNCTION NAME :  VDMTerminated    Function (8)
;*
;*  DESCRIPTION   :  Notify VDM Termination
;*
;*                   This routine receives an input data packet from the
;*                   VDD with the Screen Group ID of a VDM session that will
;*                   be terminated. As far as the PKBD is concerned we just AND
;*                   off the VDM ready bit in (fVDMReady) so we know to suspend
;*                   all future activity for that VDM. (IE. eat all scan codes
;*                   that were supposed to go to that screen group.)
;*
;*
;*  INPUT         :  CX:SI points to the VKBD input data packet below
;*
;*                      ksig_len  dw  ; length of packet (4-bytes)
;*                      ksig_sgid dw  ; screen group ID
;*
;*  OUTPUT        : NONE.
;*
;*  RETURN-NORMAL   : Bit in fVDMReady turned off for this VDM SG
;*
;*  RETURN-ERROR    : AX = 0.
;*                  BX = ParmErr if     structure length.
;*
;*  EFFECTS       :  Registers changed as needed.
;*
;*  INTERNAL REFERENCES:
;*     ROUTINES   :  SGActivation
;*
;*  EXTERNAL REFERENCES:
;*     ROUTINES   :  None.
;*     DevHelps   :  NONE.
;*
;*******************************************************************************

Public VDMTerminate
VDMTerminate Proc

   Mov ES,CX                               ;* set es to data packet pointer
  .If <ES:[SI].ksgid_len eq KSGID_PKT_LEN> ;* if length is correct
                                           ;*
      Mov  AX,ES:[SI].ksgid_sgid           ;* get screen group ID.
      Mov  BL,0                            ;* Indicate to clear Activation bit.
      Call SGActivation                    
                                     ;* Go indicate that this VDM is not active.
      Mov AX,TRUE                          ;* return success.
  .Else                                    ;*
      Xor AX,AX                            ;* clear AX to flag error
      Mov BX,ParmError                     ;* return invalid paramater error
  .Endif
   Ret

VDMTerminate Endp


;***************************************************************************
;*
;*  FUNCTION NAME  : VDMFocusChange   Function (9)
;*
;*  DESCRIPTION    :  Notification of a VDM focus change
;*
;*                 This function is called by the VKBD whenever the
;*            focus status of a VDM has changed.
;*
;*  IMPORTANT NOTE: When the VDM is loosing focus the LED state
;*  must be updated to PMs state. Numerous solutions have been proposed
;*  to fix the shift state synchronization problems and no other
;*  solutions will work. PM is designed to have only one shift state.
;*  VDMs are supposed to have a unique shift state. Windowed VDMs get
;*  get keystrokes via PM, which means that PM gets first crack at
;*  them. The VKBD is responsible for maintaining the shift states for
;*  all VDMs (windowed or fullscreen). Whenever a VDM get focus, this
;*  routine will be called to update VDMFocus to TRUE, which means that
;*  the VDMSetLeds will be the only way the hardware LED state is
;*  changed.
;*
;*  At this time the current design is as follows:
;*  - All VDMs have unique shift states managed by the VKBD
;*  - OS/2 fullscreens have a unique shift state managed by the PKBD
;*  - Everything else (VIO         and the Desktop) all use the
;*    universal PM shift state. Shift keys hit in a windowed VDM WILL
;*    effect this universal PM shift state.
;*
;*
;*
;*  INPUT      :  CX:SI points to the VKBD input packet below
;*
;*                  kf_len         DW ; length of packet (4)
;*                  kf_fsFocus     DW ; 1 - gaining focus
;*                                    ; 0 - loosing focus
;*
;*  OUTPUT     :  NONE.
;*
;*  RETURN-NORMAL:  Flag updated signifying VDM focus status.
;*
;*  RETURN-ERROR : AX = 0, BX = ParmError return code or GenError
;*               return code.
;*
;**************************************************************************

Public VDMFocusChange
VDMFocusChange Proc

  Mov ES,CX
  .If <ES:[SI].kf_len eq KFOCUS_PKT_LEN>
    Mov AX,ES:[SI].kf_fsFocus
    .if <NONZERO ax>                  ;* vdm is gaining focus
       mov VDMFocus, 1                ;*
       mov SQHotKeyData, 1            
                                      ;* Let SingleQ know Hot Key info may need
                                      ;* to be updated.
    .else                             ;* Else VDM is losing focus
       mov  VDMFocus, 0               ;*
       mov  SQHotKeyData, 2           
                                      ;* Indicate losing focus

       Mov  DI,[PSGPointers+2]        ;* Point to PMs PSG
       mov  ax, [DI].ShiftFlags       ;*
       Shr  ax,4                      ;* Move LED bits to low nibble
       And  ax,7                      ;* Turn off all unwanted bits
       Push 2222h                     ;* Var2 (Not used for this call)
       Push AX                        ;* Var1 (LED state)
       Push DD_Cmd_Indicators         ;* Function
       Call DD_Entry                  ;* Device Independent Entry Point

    .endif

    Mov AX,TRUE
  .Else
     Xor AX,AX                        ;* clear AX to flag error
     Mov BX,ParmError                 ;* return invalid paramater error
  .Endif
  Ret

VDMFocusChange Endp


;***LP  VDMQueryRevXlate - query reverse translation table
;*
;*  ENTRY
;*      (cx:si) -> KQRX packet
;*
;*  EXIT-SUCCESS
;*      return TRUE
;*  EXIT_FAILURE
;*      return FALSE - invalid code page
;*                   - invalid number of shift entries
;*
;*  USES
;*
;*  CONTEXT
;*      VDM Task
;*
;***  PCODE


;/***************************************************************************
;*
;* FUNCTION NAME  :  VDMQueryRevXlate
;*
;* DESCRIPTION    :  query reverse translation table
;*
;* INPUT          :  NONE
;*
;* OUTPUT         :  NONE
;*
;*
;* RETURN-NORMAL  : Return TRUE
;*
;*
;* RETURN-ERROR   : Return FALSE - invalid code page
;*                               - invalid number of shift entries
;*
;**************************************************************************/



Procedure VDMQueryRevXlate,near

        LocalVar f16pRXTable,DWORD
        LocalVar f16pShTable,DWORD
        LocalVar f16pNumTable,DWORD

        EnterProc

        mov     es,cx
        ASSUME  es:NOTHING

        mov     di,si                                  ;* (es:di)->KQRX

        cmp     es:[di].kqrx_len,KQRX_PKT_LEN
        je      qrx05
        jmp     qrxe

qrx05:  mov     cx,WORD PTR es:[di].kqrx_f16pRXTable
        mov     WORD PTR f16pRXTable,cx
        mov     cx,WORD PTR es:[di].kqrx_f16pRXTable+2
        mov     WORD PTR f16pRXTable+2,cx
        mov     cx,WORD PTR es:[di].kqrx_f16pShTable
        mov     WORD PTR f16pShTable,cx
        mov     cx,WORD PTR es:[di].kqrx_f16pShTable+2
        mov     WORD PTR f16pShTable+2,cx
        mov     cx,WORD PTR es:[di].kqrx_f16pNumTable
        mov     WORD PTR f16pNumTable,cx
        mov     cx,WORD PTR es:[di].kqrx_f16pNumTable+2
        mov     WORD PTR f16pNumTable+2,cx
        mov     ax,es:[di].kqrx_usCodePage
        mov     si,CPCBOFFSET                          ;* (ds:si)->CPCB[0]
        mov     bx,CPCount                             ;* (bx)=CPCount
        dec     bx                                     ;* ignore CPCB[0]
        mov     cx,1                                   ;* (cx)=CPCB index 1
        mov     ds,CBDataSel
        ASSUME  ds:NOTHING

        cmp     ax,0                              ;* code page value = 0?
        jne     qrx07                             ;* NO, continue
                                                  ;*
        mov     es:[di].kqrx_hCodePage,ax         ;* handle=slot0
        mov     ax,[si].CPValue                   ;* (ax)=code page value
        mov     es:[di].kqrx_usCodePage,ax  ;* store the default code page value
        jmp     short qrx20

qrx07:  add     si,CPCBEntrySize               ;* (ds:si)->CPCB[1]
qrx10:  cmp     [si].CPValue,ax                ;* is this the code page?
        je      qrx20                          ;* YES, found the code page
                                               ;*
        add     si,CPCBEntrySize               ;* (ds:si)->next entry
        inc     cx                             ;*
        dec     bx                       ;* decement number of code pages left
        jnz     qrx10                          ;* not end yet, continue
                                               ;*
        jmp     qrxe                           ;* code page not found
                                               ;*
qrx20:  mov     es:[di].kqrx_usCodePage,ax     ;* store codepage value
        mov     es:[di].kqrx_hCodePage,cx      ;* store codepage slot #
        mov     ds,WORD PTR [si].CPVAddr       ;*
        mov     si,HeaderLen                   ;*  (ds:si)->1st KeyDef entry
        les     di,f16pRXTable                 ;*  (es:di)->Rev. Xlate Table
        mov     ah,1                           ;*  start with scan code = 1
                                               ;*
qrx30:  mov     al,BYTE PTR [si].XlateOp       ;*  (al)=key type
        cmp     al,1                           ;*  is it alpha type?
        je      qrx50                          ;*  YES, process alpha type
                                               ;*
        cmp     al,4                           ;*  is it non-alpha symbol type?
        je      qrx50                          ;*  YES, process symbol type
                                               ;*
        cmp     al,8                           ;*  is it action type?
        je      qrx50                          ;*  YES, process action type
                                               ;*
        cmp     al,12                          ;*  is it a shift type?
        jne     qrx35                          ;*  NO, continue
                                               ;*
        jmp     qrx100                         ;*  YES, process shift type
                                               ;*
qrx35:  cmp     al,14                          ;*  is it an alt type?
        je      qrx100                         ;*  YES, process alt type
                                               ;*
        cmp     al,7                           ;*  is it num-pad type?
        jne     qrx40                          ;*  NO, continue
                                               ;*
        jmp     qrx150                         ;*  YES, process num-pad type
                                               ;*
qrx40:  add     si,KDefLen                     ;*  (ds:si)->next KeyDef entry
        inc     ah                             ;*  next scan code
        cmp     ah,128                         ;*  end of code page table yet?
        jb      qrx30                          ;*  NO, again
                                               ;*
        jmp     qrx200                         ;*  done

;Process alpha type
qrx50:  xor     bh,bh
        mov     bl,[si].Char1                  ;*  (bx)=Char1
        shl     bx,1                           ;*  (bx)=word index into rev. xlate table
        add     bx,di                          ;*  (es:bx)->rev xlate entry
        cmp     BYTE PTR es:[bx],0             ;*  is entry new?
        jne     qrx70                          ;*  NO, ignore it, go check ctrl
                                               ;*
        mov     BYTE PTR es:[bx],ah                 ;*  store scan code
        mov     BYTE PTR es:[bx+1],RXFLAGS_NOSHIFT  ;*  assume symbol type

        cmp     al,1                                ;*  alpha type?
        jne     qrx80                               ;*  NO, skip control processing

        mov     BYTE PTR es:[bx+1],RXFLAGS_LOWCASE

qrx70:  sub     bx,('a' SHL 1)-2               ;*  (es:bx)->ctrl char entry
        and     bh,01                  ;*  zero upper byte again in case wrapped
        cmp     BYTE PTR es:[bx],0             ;*  is entry new?
        jne     qrx80                          ;*  NO, ignore it, go check char2

        mov     BYTE PTR es:[bx],ah            ;*  store scan code
        mov     BYTE PTR es:[bx+1],RXFLAGS_CTRL

qrx80:  xor     bh,bh
        mov     bl,[si].Char2                      ;* (bx)=Char2
        shl     bx,1                   ;* (bx)=word index into rev. xlate table
        add     bx,di                              ;* (es:bx)->rev xlate entry
        cmp     BYTE PTR es:[bx],0                 ;* is entry new?
        jne     qrx40                              ;* NO, ignore it, next
                                                   ;*
        mov     BYTE PTR es:[bx],ah                ;* store scan code
        mov     BYTE PTR es:[bx+1],RXFLAGS_UPCASE  ;* assume alpha type

        cmp     al,1                               ;* alpha type?
        je      qrx40                              ;* YES, done, next
                                                   ;*
        mov     BYTE PTR es:[bx+1],RXFLAGS_SHIFT   ;* assume symbol type
        cmp     al,4                               ;* symbol type?
        je      qrx40                              ;* YES, done, next

        mov     BYTE PTR es:[bx+1],RXFLAGS_CTRL
        jmp     short qrx40                      ;*  done, next

;* Process shift type
qrx100: les     di,f16pShTable           ;*  (es:di)->Shift table
        mov     cx,NUM_SHENTRIES         ;*  (cx)=number of shift table entries
        mov     bx,WORD PTR [si].Char1   ;*  (bx)=shift flags
        mov     al,BYTE PTR [si].Char3   ;*  (al)=high byte of shift flags with E0
                                                 ;*
qrx110: cmp     es:[di].sh_scanShift,0           ;*  new entry?
        jne     qrx130                           ;*  NO, next
                                                 ;*
        cmp     es:[di].sh_fsShift,bx            ;*  matching entry?
        jne     qrx120                           ;*  NO, go check prefixed shift
                                                 ;*
        mov     es:[di].sh_scanShift,ah          ;*  store scan code
        jmp     short qrx130                     ;*  next
                                                 ;*
qrx120: xchg    bh,al                            ;*  (bx)=prefixed shift flags
        cmp     es:[di].sh_fsShift,bx            ;*  matching entry?
        jne     qrx130                           ;*  NO, next
                                                 ;*
        mov     es:[di].sh_scanShift,ah          ;*  store scan code

qrx130: add     di,SHENTRY_SIZE                  ;*  (es:di)->next entry
        dec     cx                               ;*  done yet?
        jnz     qrx110                           ;*  again

        les     di,f16pRXTable                   ;*  (es:di)->Rev xlate table
        jmp     qrx40

;Process NumPad type
qrx150: les     di,f16pNumTable                  ;*  (es:di)->NumTable
        xor     bh,bh
        mov     bl,[si].Char2                    ;*  (bx)=NumChar
        sub     bx,'0'                           ;*  (bx)=index into numtable
        jb      qrx160                           ;*  not a number

        cmp     bx,10                            ;*  within range
        jae     qrx160                           ;*  not a number

        add     bx,di                            ;*  (es:di)->num entry
        mov     BYTE PTR es:[bx],ah              ;*  store scan code

qrx160: les     di,f16pRXTable
        jmp     qrx40

qrx200: mov     ax,CTRUE                         ;*  return TRUE

qrxx:   LeaveProc
        ret

qrxe:   xor     ax,ax                            ;*  return FALSE
        jmp     short qrxx

EndProc VDMQueryRevXlate


;**************************************************************************
;*
;*  FUNCTION NAME  : VDMHotKeyControl Function (11)
;*
;*  DESCRIPTION    : NOTIFY PKBD OF CTRL KEY SUPPORT
;*
;*                   This function is called by the VKBD to notify PKBD
;*                   of which hot keys should be bypassed or enabled.  This is
;*                   per session and does NOT have a global effect on Hot Keys.
;*
;*  NOTES         :  This is called whenever a VDM gets or loses focus.
;*
;*  INPUT         :  CX:SI points to the VKBD input packet KHKEY
;*
;*                    length         DW ; length of packet (8)
;*                    action         DW ; 0 - enable control keys
;*                                      ; 1 - bypass control keys
;*                    hot_keys       DD ; 0 = all control key sequences
;*                                      ; bit 0 = Alt+Esc
;*                                      ; bit 1 = Ctrl+Esc
;*                                      ; bit 2 = Alt+Enter
;*                                      ; bits 3-31 = not used yet
;*
;*  OUTPUT        :  NONE.
;*
;*  RETURN-NORMAL   : System Hot Keys are enabled or bypassed for the session
;*                  in which call was made.
;*
;*  RETURN-ERROR    :  AX = 0, BX = ParmError return code
;*
;*  EFFECTS       : CTRL key events are enabled or bypassed.
;*
;*
;***************************************************************************

;**          

Public VDMHotKeyControl
VDMHotKeyControl     Proc

.386p
   Push EAX                                   
   Mov ES,CX
  .If <ES:[SI].khk_len eq KHOTKEYS_PKT_LEN>   ;* If the length is correct
     Mov AX,ES:[SI].khk_action                ;* Get the action

    .If <ax eq 0>                             ;* If we should ENABLE control keys
       Mov EAX, dword ptr ES:[SI].khk_hotkeys ;* Get the Hot Key bits
      .If <eax eq 0>                          ;* If Enable ALL
        ;*if <VDMFocus eq 1>                  ;* If VDM is in FOCUS
           Mov HKBypass, 0                    ;* Zero out HKBypass
        ;*else
        ;*  Mov HKBypass, 4
        ;*endif
      .Else                                   ;* Else just enable what was passed
        ;*If <eax eq 4> AND                   ;* Terminate or Lose Focus CASE
        ;*If <VDMFocus eq 0>                  ;* Make sure VDM NOT the focus
        ;*  Mov HKBypass, 4
        ;*Else
           Not eax                            ;* Complement EAX
           AND HKBypass, eax                  ;* Clear the bits passed
        ;*Endif                               ;*
      .Endif                                  ;*
                                              ;*
    .Else                                     ;* Else BYPASS certain control keys
       Mov EAX, dword ptr ES:[SI].khk_hotkeys ;* Get the Hot Key bits
                                              
       And HKBypass, 0FFFBh                   ;* Clear alt+home bit, in case
                                              ;* it was set when NON-VDM in focus

      .If <eax eq 0>                          ;* If BYPASS ALL
         Xor AX,AX                            ;* ERROR, Clear AX to flag error
         Mov BX,ParmError                     ;* return invalid paramater error
      .Else                                   ;*
         push eax                             ;* Save the hotkey bits
         Or eax, HKBypass                     ;* OR existing bits into passed bits
         and eax, 0003h                       ;* Mask out all but bit 0 and 1
        .If <ax eq 03h>                       ;* If attempt to bypass Alt+Esc
                                              ;* and Ctrl+Esc (bit 0 and 1)
           pop eax                            ;* Clean up the stack
           Xor AX,AX                          ;* ERROR, clear AX to flag error
           Mov BX,ParmError                   ;* Return invalid paramater error
        .Else                                 ;* Else valid parms
           pop eax                            ;* Get hotkey bits back
           or HKBypass, eax                   ;* Set the bits
        .Endif                                ;*
      .Endif                                  ;*
    .Endif                                    ;*
                                              ;*
  .Else                                       ;* Else     LENGTH parm
     Xor AX,AX                                ;* Clear AX to flag error
     Mov BX,ParmError                         ;* Return invalid paramater error
  .Endif
   Pop EAX                                    
.286p
   Ret

VDMHotKeyControl  Endp

Code    ENDS

        END
