;*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   KBDIOCAB - IOCTL CATEGORY "A" and "B" Routines
     Name    KBDIOAB

;/*************************************************************************
;*
;* SOURCE FILE NAME = KBDIOAB.ASM
;*
;* DESCRIPTIVE NAME = PKBD CATEGORY "A" and "B" IOCTLS
;*
;*
;* VERSION      V2.0
;*
;* DATE         02/11/92
;*
;* DESCRIPTION  This file contains the subroutines that handle the CATEGORY
;*              A and B IOCTLs.
;*
;* FUNCTIONS    SGController()    System activities/modifications notifications
;*                                for device drivers.
;*              MonRegister()     Registers the Monitor passed by the caller of
;*                                of the IOCTL.
;*
;*
;* NOTES       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
;*   04-02-90              B788807 Do not set CurSG until AFTER calling Update_SG_SS
;*   08-19-92              52995   Do not block sgcontroller forever.
;*
;*
;*
;**************************************************************************



.286p
.xcref
.sall

;* The following files are included, but XLIST'd:

.xlist
  Include       basemaca.inc             ;* DOS macros.
  Include       osmaca.inc               ;* Macro file for OS/2 kernel
  CPUMODE       286                      ;*
  Include       struc.inc                ;* Structured assembly macros.
  Include       devhlp.inc               ;* DevHlp & Signal equates, char queue structure.
  Include       devsym.inc               ;* Device driver header definitions.
  Include       infoseg.inc              ;* InfoSeg structure.
  Include       singleq.inc              ;* Single Queue Device Driver equates.
  Include       kbdseg.inc               ;* Segment definitions.
  Include       kbdddr.inc               ;* Keyboard Device Driver structures & equates.
  Include       smrextrn.inc             ;* Session Man. and
  Include       pmshl.inc                ;* Session type includes
  Include       kbdaim.inc               ;* AIM include file
.list

    ;*
    ;** Set up to use the local variables on the stack.  BP must NOT be
    ;** altered if these vars. are to be used.
    ;*

StkFrameVars                                     ;* Macro to set up stack variables.

;*
;**  External Routines
;*

    EXTRNFAR    AccessKCB                        
    EXTRNFAR    FreeKCB                          ;*         THESE
    EXTRNFAR    Unlock_CustomCP                  ;*         ARE
    EXTRNFAR    PutInSQB                         ;*         ALL
    EXTRNFAR    Update_SG_SS                     ;*         HYBRIDS.
    EXTRNFAR    PurgeKIB                         ;*         (CALLABLE FROM KBDCODE OR
    EXTRNFAR    DiscardElement                   ;*         SWAPCODE SEGMENTS.)
    EXTRNFAR    SGActivation                     

    EXTRNFAR    UpdStickyTbl                     ;* AIM
    EXTRNFAR    TypaControl_Reset                ;* AIM
    Extrn       TypaControl_Disable  :Far        ;* AIM
    Extrn       AttachToMouse        :Far        ;* AIM

    Extrn       SearchCPCB           :Near
    Extrn       InitPSG              :Near
    Extrn       MonDispEntry         :Near
    Extrn       IOCtlAddrCheck       :Near
    Extrn       DD_Entry             :Far

;*
;** External Variables
;*

    Extrn      IntFlagArea             :Byte
    Extrn      MiscFlags2              :Byte
    Extrn      CmdFlags                :Byte
    Extrn      MiscFlags3              :Byte
    Extrn      KeyPacket1              :Word
    Extrn      CurSG                   :Word
;   Extrn      SwitchPID               :Word
    Extrn      PSGPointers             :Word
    Extrn      MaxSG                   :Word
    Extrn      CBDataSel               :Word
    Extrn      SQDDpds                 :Word
    Extrn      DeviceHelp              :DWord
    Extrn      SQDDpm@                 :Dword
    Extrn      ScanBlockId             :Word     
    Extrn      SCCount                 :Byte     
    Extrn      IntFlags                :Byte     
    Extrn      LInfoSeg                :Dword
    Extrn      KeyHistBuf              :Byte     ;*           

    Extrn      AIMTimeOut              :Word     ;* AIM
    Extrn      AIMAccept               :DWord    ;* AIM
    Extrn      AIMRate                 :DWord    ;* AIM
    Extrn      AIMDelay                :DWord    ;* AIM
    Extrn      AIMFlags                :Word     ;* AIM
                                                 ;*
    Extrn      LockHandle              :byte     ;* AIM
    Extrn      End_AIMCode             :byte     ;* AIM
    Extrn      Start_AIMCode           :byte     ;* AIM

StratCode Segment 

   Assume CS:StratCode,DS:DGROUP,ES:Nothing,SS:Nothing

BREAK <SGController routine>
Public SGController
SGController Proc Far

;***************************************************************************
;*
;* FUNCTION NAME  : SGController   (IOCTL 41h Cat. Bh)
;*
;* DESCRIPTION    : System activities/modifications notifications
;*                  for device drivers.
;*
;*            This IOCtl is called for several actions.
;*
;*          O   Actions involving screen group control:
;*              Group Creation, Termination, and Switch
;*
;*              When switching to or from a VDM session, this routine
;*              will update the PKBD's current screen group number to
;*              reflect the VDM.  Since there are no PSG data areas
;*              for the VDM sessions, this routine will point to the
;*              PSG data area for screen group 2, (old 3xBox), until
;*              it again switches to another FS or the PM session.
;*
;*          O   Actions involving AIM (Alternate Input Method):
;*              AIM pre save verify, AIM post save activate
;*
;*              The first AIM Pre Save action called for will
;*              attempt to get an IDC entry point to the Mouse DD.
;*              Parm checking is also done for this action.
;*
;*              When AIM Post Save action is called the Filter Key
;*              parms are saved and Filter/Sticky Key support is rese
;*              if AIM support is to be made active.  If AIM support
;*              is made inactive, Filter/Sticky Key support is
;*              disabled.
;*
;* NOTE:        If there is a monitor chain in a SG it will not be
;*              removed on a SG Termination request, since the
;*              processes owning the monitors in that chain may be
;*              in another SG and may wish to remain registered in
;*              case the SG becomes active again later.
;*
;* INPUT      :
;*               REGISTERS:
;*
;*                  ES:BX = Ptr to Request Block
;*
;*   The Request Packet contains a pointer to the parm list
;*   containing the following:
;*
;*            Length - 1 Word (Structure Length in Bytes)
;*            Action - 1 Word (Action Bit Mask as doc'd by SMG)
;*            CallData - Length dependant on the Action
;*
;* HEX Parm:  When ACTION is:                CALLDATA is:
;*
;* 0010h      Creation_Notification          New_SG         WORD
;*                                           New_Type       WORD
;*
;*
;* 0008h      Termination_Notification       Outgoing_Type  WORD
;*                                           Outgoing_SG    WORD
;*
;* 0020h      Pre_Switch_Notification        Incoming_SG    WORD
;*                                           Incoming_Type  WORD
;*                                           Outgoing_SG    WORD
;*                                           Outgoing_Type  WORD
;*
;* 0080h      AIM_Pre_Save_Notification      AIM_Errors     DWORD
;*                                           AIM_Active     WORD
;*                                           AIM_TimeOut    WORD
;*                                           AIM_FKAccept   DWORD
;*                                           AIM_FKRate     DWORD
;*                                           AIM_FKDelay    DWORD
;*
;* 0100h      AIM_Post_Save_Notification     Same as above
;*
;* EXIT-NORMAL : Return to main Strategy processing.
;*
;* EXIT-ERROR  : If invalid parm, length, or action
;*
;* EFFECTS     : CurSG and/or SGCreatedTbl are updated.
;*               AIM segment may be locked or unlocked depending
;*               on if AIM is made active or inactive respectively.
;*               If AIM is made active, Filter Key and Sticky Key
;*               support may adversely affect standard keyboard
;*               acceptance, typamatic rate, delay, and key up/down
;*               features.
;*
;* INTERNAL REFERENCES:
;*    ROUTINES:
;*               DiscardElement          DD_Cmd_Entry
;*               PurgeKIB                AccessKCB
;*               Unlock_CustomCP         IOCtlAddrCheck
;*               SGActivation            InitPSG
;*               Update_SG_SS            PutInSQB
;*               AttachToMouse           TypaControl_Enable
;*               UpdStickyTbl
;*
;* EXTERNAL REFERENCES:
;*    ROUTINES:
;*               DevHlp_ProcRun    DevHlp_ProcBlock
;*
;*****************************************************************************
;* PSEUDO-CODE
;*
;* If some scan code breaks have not occurred yet
;*   Block until all the breaks occur
;* Endif
;*
;* Disable the Keyboard
;*
;* Get the following information from the IOCtls request packet:
;* Length
;* Action Type
;* Notification data (specific to the type)
;*
;* If Creation Notification
;*   If fullscreen is the type
;*        CALL SGActivation to Indicate that this session is created
;*   Endif
;*
;* Elseif Termination Notification
;*   Point to PSG for the OutGoing SG
;*      CALL SGActivation to indicate session is terminated
;*         If fullscreen is the type
;*            Clear all PSG flags.
;*            Specify registers to hold KCB address
;*            CALL AccessKCB to get desired KCB address
;*            CALL Unlock_CustomCP to Unlock KCB's custom code page,
;*                                                    if needed
;*            Get pointer to oldest KIB element for this KCB
;*            If the KIB is NOT empty
;*              Get segment containing KIB
;*              Set up to discard element
;*              CALL DiscardElement to do so
;*            Endif
;*            CALL PurgeKIB to purge rest of KIB
;*            Call InitPSG go initialize sessions PSG data
;*         Endif
;*
;* ElseIf Pre Switch Notification
;*   Point to PSG for the OutGoing SG
;*   Mark the session as non-active.
;*   CALL AccessKCB to access the outgoing KCB,
;*                                    for Update_SG_SS use later
;*   Point to PSG for the InComing SG.
;*   Indicate in the PSG Flags SG is now active
;*   CALL Update_SG_SS to update the shift state for new and old SGs
;*   If thread assoc with MonEventID
;*     Issue Dev_Hlp ProcRun on it
;*   Endif
;*
;*   If Screen Group not already in use
;*     Get DBCS shiftflags from previous SG
;*     If a cusXT has NOT been loaded for this SG
;*
;*       If optional codepages have been enabled
;*           Set default code page index, CPCB(1)
;*       Else code pages not enabled yet,
;*           Use table in CPCB(0)
;*       Endif
;*
;*     Endif
;*     Clear shift and LED flags for new screen group
;*     Copy DBCS shiftflags from previous screen group
;*     If this is the PSG used for all FS VDM's
;*        Indicate so
;*     Endif
;*
;*     Indicate we've done this IOCtl at least once
;*     Indicate in the PSG Flags SG is now active
;*
;*   Endif first time check
;*
;*   Get incoming PSG offset
;*   CALL AccessKCB to access it's KCB
;*   Put shift flags from the current KCB into the PSG
;*
;*   Clear Alt-nnn accumulator
;*   Point to KIB data segment
;*   Adjust to the KIB's header
;*   Get number of KIB elements available
;*   Make it the maximum for this session
;*   Get shift flags of new PSG
;*   Get caller's PSG offset of the new current session
;*   Save caller's shiftflags
;*   If not in the PM SG
;*      Turn off all unwanted bits
;*      Set up parms for DD_Cmd_Indicators
;*      Call DD_Cmd_Entry
;*   Else, in the PM SG
;*      Load pointer to current PSG
;*      Get address of KeyPacket1
;*      CALL PutInSQB to send it to PM
;*   Endif PM SG or not
;*
;* Elseif AIM Pre Save Notification
;*
;*     If this is the first AIM request
;*        Indicate First Aim Request occured
;*        Call AttachToMouse to get an IDC entry point to the Mouse$
;*     Endif
;*
;*     Check the Active, Time Out, Accept, Rate, and Delay parms
;*     If any errors
;*       Indicate in callers error field the invalid parms
;*       Indicate parm check was not successful
;*     Else parms are OK
;*       Indicate in callers error field no parm errors
;*       Indicate parm check was successful
;*     Endif
;*
;* Elseif AIM Post Save Notification
;*
;*     If AIM support is to be made active
;*        If AIM segment not already locked
;*           Lock Down AIM segment
;*           If Lock Down Failure
;*             Indicate General Failure Error
;*           Endif
;*           Set flag to indicate Aim segment locked down
;*        Endif
;*        Set flag to indicate AIM ACTIVE
;*        Store AIM Timeout data
;*        Store AIM FKAccept data
;*        Store AIM FKRate data
;*        Store AIM FKDelay data
;*        Call TypaControl_Enable to reset Filter Keys
;*        Call UpdStickyTbl to reset Sticky Keys
;*     Elseif AIM support is to be made inactive
;*        Call TypaControl_Disable to disable Filter Keys
;*        Call UpdStickyTbl to Disable Sticky Keys
;*        Set flag to indicate AIM NOT ACTIVE
;*        Unlock the AIM segment
;*        Clear flag to indicate AIM segment locked
;*     Endif
;*
;* Else none of above cases were true
;*    Restore request packet address.
;*    Indicate invalid parameter in RP
;* Endif
;*
;* Enable the keyboard
;*
;* Ret
;********************************************************************


; Mov AX, StkPID                                 ;* Get caller's Process ID.
;.If <SwitchPID eq 0>                            ;* Is this the first SG change?
;  Mov SwitchPID, AX                             ;* Yes, save caller's PID.
;.Endif                                          ;*
                                                 
 .If <SCCount ne 0>                              ;* If sccount is nonzero
                                                 ;* Then we must block until all
                                                 ;* interrupts come in for present
                                                 ;* session.
    cli
    Pusha                                        ;* Save regs
    Or    IntFlags, ScanBlockSet                 ;* Set flag
    Mov   BX,offset ScanBlockId                  ;* Get Block ID
    Mov   ScanBlockId, BX                        ;* Save it
    Mov   AX,DS                                  ;* Get Block ID
    Mov   [ScanBlockId+2], AX                    ;* Save it
;             Mov CX,-1                          ;* Indicate to...
;             Mov DI,CX                          ;* ...never timeout.
    Mov   CX,3000                                ;*            wake up after 3 seconds.
    Xor   DI,DI                                  ;*           
    Xor   DH,DH                                  ;* Indicate okay to interrupt.
    Mov   DL,DevHlp_ProcBlock                    ;* Set ProcBlock DevHlp function.
    Call  [DeviceHelp]                           ;* Go block the caller.
    sti
    Popa                                         ;* Restore regs
    And   IntFlags, NOT ScanBlockSet             
    .If <SCCount ne 0>                           ;*             If sccount is nonzero
        Mov    SCCount, 0                        ;*             reset sccount
        Mov    KeyHistBuf, 0                     ;*             clear out the KeyHistBuf
        Mov    KeyHistBuf+1,0                    ;*           
        Mov    KeyHistBuf+2,0                    ;*           
        Mov    KeyHistBuf+3,0                    ;*           
        Mov    KeyHistBuf+4,0                    ;*           
        Mov    KeyHistBuf+5,0                    ;*           
        Mov    KeyHistBuf+6,0                    ;*           
        Mov    KeyHistBuf+7,0                    ;*           
        Mov    KeyHistBuf+8,0                    ;*           
        Mov    KeyHistBuf+9,0                    ;*           
    .Endif                                       ;*           

 .Endif                                          
                                                 ;* The local storage on the stack
                                                 ;*  also contains the addresses of
                                                 ;*  the caller's PSG and KCB.
    Push 2222h                                   ;* Var2 (Not used for this call)
    Push 1111h                                   ;* Var1 (Not used for this call)
    Push DD_Cmd_Disable                          ;* Disable Function.
    Call DD_Entry                            ;* Device Independent Entry Point
                                                 ;*
    LES BX,Dword Ptr StkRPO                      ;* Get request packet addr.
    LES SI,ES:[BX+ParmListPtr]                   ;* Get address of parm list.
    Mov BX,ES:[SI+2]                             ;* Get Action Type
    Mov AX,ES:[SI+4]                             ;* Get Notification data
    Mov CX,ES:[SI+6]                             ;* Get Notification data
                                                 ;*
 .If <BX eq Creation_Notification>               ;* If Session Manager indicates to
                                                 ;* Create a new session.
                                                 ;* AX=New_SG    CX=New_Type
    .If <CX eq 1>                                ;* Mark Creation only for PROG_FULLSCREEN
                                                 
                                                 ;* AX is SG to Create.
       Mov AH,0                                  ;* Clear SG Proc type.
       Mov  BL, 1                                ;* Parm for activating.
       CALLFAR SGActivation                      ;* Indicate that this session is

    .Endif

 .Elseif <BX eq Termination_Notification>
                                                 ;* If Session Manager wants termination
     Mov DI,CX                                   ;* Put session number in index reg.
    .If <DI ge sgidFirstVDM>                     ;* If a VDM SGID
       Mov DI,DOS3xBoxSG                         ;* We will be using SG 2 for VDMs
    .Endif                                       ;*
     Shl DI,1                                    ;* Make it an offset.
     Mov DI,[DI+PSGPointers]                     ;* Point to PSG for the OutGoing SG.
                                                 ;* terminate the session.
        Push AX                                  ;* Save Type
        Mov  AX,CX                               ;* Get SG to terminate.
        Mov  BL, 0                               ;* Setup to clear, AX SG to Term.
        CALLFAR SGActivation                     ;* Indicate that this session is
        Pop  AX                                  ;* Restore Type
       .If <AX eq 1>                             ;* Do Termination only for FS
           Push DI                               ;* Save offset of PSG to Terminate.
           Mov  [DI].PSGFlags,0                  ;* Clear all PSG flags.
           Mov  DH,ES_DI                         ;* Specify registers to hold KCB
           CALLFAR AccessKCB                     ;* desired KCB address.
           CALLFAR Unlock_CustomCP               ;* Unlock the KCB's custom code
                                                 ;* page, if necessary.
           Push ES                               ;* Put KCB value in DS for
           Pop  DS                               ;* following processing.
           Mov  SI,DI                            ;* IBID for SI and DI.
           Mov  AX,[SI].KCBOutPtr                ;* Get pointer to front of queue.
                                                 ;*
          .If <NONZERO ax> AND                   ;* If queue head is not element 0
          .If <AX ne -1>                         ;* AND the KIB is NOT empty:
              Mov  ES,StkCBData                  ;* Get segment containing KIB.
              Push AX                            ;* Save KIB element to be freed
              Dec  AX                            ;* Calculate the element's
              IMul AX,KIBElLen                   ;* offset into the free list.
              Add  AX,FLISTOFFSET                ;* adjust for beginning of the
                                                 ;* free list elements in the
                                                 ;* segment containing the KIB.
              Mov  DI,AX                         ;* Put element index into an
                                                 ;* index register.
                                                 ;* (ES:DI -> KIB Element to be freed)
              Mov  AX,ES:[DI].KIBRevPtr          ;* Get the index to the next
                                                 ;* element in the queue.
              Mov  [SI].KCBOutPtr,AX             ;* Make the next element in the
                                                 ;* KIB the head of the queue.
              Pop  AX                            ;* Restore the KIB element
                                                 ;* index to be freed.
              Pop  SI                            ;* Get OutGoing PSG pointer
              Push SI                            ;* Save it again for stack balancing.
              Mov  DS,StkDS                      ;* Get Keyboard DD Data segment
              CALLFAR DiscardElement             ;* Go release this element.

          .EndIf

           Mov  DS,StkDS                         ;* Restore DD's Data Segment.
           Pop  DI                               ;* Restore OutGoing PSG offset.

           Push AX                               
           Xor  AX,AX                            ;* Indicate to Access the KCB
           CALLFAR PurgeKIB                      ;* purge rest of KIB.
           Pop  AX                               
           Call InitPSG                          ;* Go initialize sessions PSG data.

       .Endif                                    ;* Endif Termination request for
                                                 ;* Full Screen Protect Mode.

 .ElseIf <BX eq Pre_Switch_Notification> NEAR
                                                 ;* ElseIf the Session Manager called
        Mov DI,ES:[SI+8]                         ;* Get outgoing_sg in index reg
       .If <DI ge sgidFirstVDM>                  ;* If a VDM SGID
          Mov DI,DOS3xBoxSG                      ;* We will be using SG 2 for VDMs
       .Endif

        Shl DI,1                                 ;* Make it an offset.
        Mov DI,[DI+PSGPointers]                  ;* Point to PSG for the OutGoing SG.
                                                 ;* for us to switch sessions.
                                                 ;* (DS:DI = OutGoing Session's PSG).
        And [DI].PSGFlags,Not ActiveSG           ;* Mark the session as non-active.
        Mov DH, ES_DI                            ;* (ES:DI = OutGoing Session's KCB).
        CALLFAR AccessKCB

        CALLFAR Update_SG_SS                     ;* Update the shift state for the


        Mov CurSG, AX                            ;* Save new Session number
        Mov SI,AX                                ;* Put Session number in index reg.

       .If <SI ge sgidFirstVDM>                  ;* If a VDM SGID
          Mov SI,DOS3xBoxSG                      ;* We will be using SG 2 for VDMs
       .Endif

        Shl SI,1                                 ;* Make it an offset.
        Mov SI,[SI+PSGPointers]                  ;* Point to PSG for the InComing SG.
                                                 ;* (DS:SI = InComing Session's PSG).
                                                 ;* (DS:SI = InComing Session's PSG).
        Or [SI].PSGFlags,ActiveSG                ;* Desired SG is now active
;*       CALLFAR Update_SG_SS                    ;* Update the shift state for the
                                                 ;* new and old SGs
                                                 ;* If thread assoc with MonEventID

       .If <<Word Ptr [SI].MonEventID> ne 0>
          SaveReg<ax,bx,dx>                      ;* issue Dev_Hlp ProcRun on it.
          Mov  AX,Word Ptr [SI].MonEventID+2     ;* move high word of MonEventID.
          Mov  BX,Word Ptr [SI].MonEventID       ;* move low word of MonEventID.
          Mov  DL,DevHlp_ProcRun                 ;* set ProcRun function.
          Call [DeviceHelp]                      ;* go do it.
          Mov  Word Ptr [SI].MonEventID,0        ;* zero out the double word
          Mov  Word Ptr [SI].MonEventID+2,0      ;* on MonEventID
          RestoreReg<dx,bx,ax>
       .Endif

       .If <bit [SI].PSGFlags z SGInUse> NEAR    ;* If not already in use.
          Mov CL, ES:[DI].KCBDBCSSh              ;* Also get DBCS shiftflags.

          ;*
          ;** DS:SI = Desired (i.e., new current) session's PSG.  The
          ;** default KCB within the PSG is used for a new session.
          ;** When switching screen groups for the first time we must find
          ;** out if the codepages defined in the CONFIG.SYS file
          ;** were ever enabled.
          ;*
                                                 
          Test [SI].DKCB.KCBFlags, 1             ;* If a cusXT has NOT been loaded for
         .If <z>                                 ;* this session before it has become
                                                 ;* foreground for the 1st time.
                                                 

            .If <bit CmdFlags nz CP_ENABLED>

                                                 ;* If opt. CP's have
                                                 ;* been enabled.
               Mov [SI].DKCB.KCBIxOffCP,1        ;* Set default code page index
                                                 ;* to entry 1 of the CPCB.
            .Else                                ;* Code pages not enabled yet,
               Mov [SI].DKCB.KCBIxOffCP,0        ;* so use table in CPCB(0).
            .Endif                               ;* EndIf CP's were enabled.
                                                 
         .Endif                                  

                                                 
          Xor DX,DX                              ;* Clear the shift and LED flags
          Mov [SI].DKCB.KCBShift,DX              ;* for the new screen group.
          Mov [SI].ShiftFlags,DX                 
          Mov [SI].DKCB.KCBDBCSSh,CL             ;* Copy DBCS shiftflags from
                                                 ;* previous screen group.
         .If <SI e DOS3xBoxSG>                   ;* If this is the PSG used
                                                 ;* for all FS VDMs
              Or [SI].PSGFlags,SG3xBox           ;* Indicate so.
         .Endif                                  ;*

          Or MiscFlags3,IOCTl1st                 
                                                 ;* done this IOCtl at least once.
          Or [SI].PSGFlags,SGInUse               ;* Indicate now initialized.
                                                 ;*
       .Endif                                    ;* End if first time check.

        Mov DI,SI                                ;* Put PSG offset in reg., set
        Mov DH,ES_DI                             ;* variables for AccessKCB.
        CALLFAR AccessKCB                        ;* Returns ES:DI = new current
                                                 ;* KCB.
        Mov AX,ES:[DI].KCBShift                  ;* Put shift flags from the
        Mov [SI].ShiftFlags,AX                   ;* current KCB into the PSG.

        Mov IntFlagArea.XAltKeyPad,0             ;* Clear Alt-nnn accumulator.
        Mov ES,StkCBData                         ;* Point to KIB data segment.
        Mov DI,FLISTHEADER                       ;* Adjust to the KIB's header.
        Mov AX,ES:[DI].ElsInList                 ;* Get number of KIB elements
                                                 ;* available.
        Mov ES:[DI].MaxThisTime,AX               ;* Make it the maximum for this
                                                 ;* session.

                                                 ;* (DS:SI = InComing PSG)
        Mov DX,[SI].ShiftFlags                   ;* Get shift flags of new PSG.
        Mov DI,StkPSG                            ;* Get caller's PSG offset.
                                                 ;* of the new current session.
        Push [DI].ShiftFlags                     ;* Save caller's shiftflags.
        Mov [DI].ShiftFlags,DX                   ;* new SG shiftflags there for a bit.

       .If <bit [SI].PSGFlags z SQMODE>          ;* If not in the PM SG
           Shr  DX,4                             ;* Move LED bits to low nibble
           And  DX,7                             ;* Turn off all unwanted bits
           Push 2222h                            ;* Var2 (Not used for this call)
           Push DX                               ;* Var1 (LED state)
           Push DD_Cmd_Indicators                ;* Function
           Call DD_Entry                     ;* Device Independent Entry Point

       .Else                                     ;* If in the PM SG
           SaveReg<BX,DI,SI>                     ;* Save all used Regs
           Mov DI,SI                             ;* Load pointer to current PSG
           Lea SI,KeyPacket1                     ;* Get address of KeyPacket1
           CALLFAR PutInSQB                      ;* Send it to PM
           RestoreReg<SI,DI,BX>                  ;* Restore all modified Regs

       .Endif                                    ;* EndIf PM SG or not.

        Pop [DI].ShiftFlags                      ;* Restore caller's shiftflags.
        Even                                     ;* Ensure we're on word bound.


                                                 
.386p                                            

 .Elseif <BX eq AIM_Pre_Save_Notification> NEAR  ;*  If SM wants AIM pre save verify

    Push EAX

   .If <bit AIMFlags z FirstAimReq>              ;* AIM If this is 1st aim request received
      Or AIMFlags, FirstAimReq                   ;* AIM Indicate first aim request ocurred
      Mov CX, FilterAttach                       ;* AIM Get one IDC entry point mouse
      Call    AttachToMouse                      ;* AIM  handle for Filter processing
      Mov CX, StickyAttach                       ;* AIM Get one IDC entry point mouse
      Call    AttachToMouse                      ;* AIM  handle for Sticky Key processing
   .Endif

    Add SI,4                                     ;* AIM Adjust SI to point to Notification
    Xor EAX,EAX                                  ;* AIM Clear EAX

   .If <bit ES:[SI.AIM_Active] nz AIM_ActiveMask>
                                                 ;* AIM If any bits other than LSB are on
     Or EAX,AIM_ActiveErr                        ;* AIM Invalid parm, set error bit
   .Endif

   .If <ES:[SI.AIM_TimeOut] lt 0> OR             ;* AIM If less than zero OR
   .If <ES:[SI.AIM_TimeOut] gt AIM_TimeOutMax>   ;*
                                                 ;* AIM If TimeOut value is too large
      Or EAX, AIM_TimeOutErr                     ;* AIM Invalid parm, set error bit
   .Endif

   .If <ES:[SI.AIM_FKAccept] lt 0> OR            ;* AIM If less than zero OR
   .If <ES:[SI.AIM_FKAccept] gt AIM_AcceptMax>
                                                 ;* AIM If Acceptance value too large
      Or EAX, AIM_AcceptErr                      ;* AIM Invalid parm, set error bit
   .Endif

   .If <ES:[SI.AIM_FKRate] lt 0> OR              ;* AIM If parm is less than zero OR
   .If <ES:[SI.AIM_FKRate] gt AIM_RateMax>
                                                 ;* AIM If parm is greater than 10000 ms
      Or EAX, AIM_RateErr                        ;* AIM Invalid parm, set error bit
   .Endif

   .If <ES:[SI.AIM_FKDelay] lt 0> OR             ;* AIM If parm is less than zero OR
   .If <ES:[SI.AIM_FKDelay] gt AIM_DelayMax>
                                                 ;* AIM If parm is greater than 10000 ms
      Or EAX, AIM_DelayErr                       ;* AIM Invalid parm, set error bit
   .Endif

    Mov ES:[SI.AIM_Errors], EAX                  ;* AIM Put error bits in callers field
    Pop EAX

 .Elseif <BX eq AIM_Post_Save_Notification> NEAR
                                                 ;* AIM If SM wants AIM post save activate
   .If <bit ES:[SI.AIM_Active+4] nz 0001h> NEAR  ;* AIM If AIM support to be active

     .If <bit AIMFlags z AIMLocked>              ;* Aim If not already locked
                                                 ;* AIM BEGIN Lock down AIMCode segment
         SaveReg <EAX,EBX,ECX,EDX,ESI,EDI,DS,ES> 


         Mov     AX, AIMCode                     
         Lea     ESI, Start_AIMCode
         Mov     DL, DevHlp_VirtToLin
         Call    [DeviceHelp]

         Mov     EDI, EAX
         Mov     AX, DS
         Mov     ESI, offset LockHandle
         Mov     DL, DevHlp_VirtToLin            ;* AIM do a virttolin DevHelp (lockhandle)
         Call    [DeviceHelp]


         Mov     ESI, EAX                        ;* AIM get offset to lock handle
         Mov     EAX, 16                         ;* AIM long term lock
         Mov     EBX, EDI                        ;* AIM get offset to area to lock
         Mov     EDI, -1                         ;* AIM set to no page list
         Lea     ECX, end_AIMCode                ;* AIM size to lock
         Sub     ECX, offset Start_AIMCode       ;* AIM size to lock
         Mov     DL, DevHlp_VMLock               ;* AIM do a VMLock DevHelp
         Call    [DeviceHelp]                    ;* AIM

         RestoreReg <ES,DS,EDI,ESI,EDX,ECX,EBX,EAX>  ;* PB732916

        .If <c>
            Jmp ExitGenFail
        .Endif
         Or AIMFlags, AIMLocked                  ;* AIM AIMCode segment locked
     .Endif                                      ;* AIM END Lock down AIMCode segment

      Push EAX
      Or   AIMFlags, AIMActive                   ;* AIM Indicate that AIM is ACTIVE
      Add  SI,4                                  ;* AIM Adjust SI to point to Notification
      Mov  AX, Word Ptr ES:[SI.AIM_Timeout]      ;* AIM Save
      Mov  AIMTimeOut, AX                        ;* AIM the
      Mov  EAX, DWord Ptr ES:[SI.AIM_FKAccept]   ;* AIM AIM
      Mov  AIMAccept,  EAX                       ;* AIM Parameter
      Mov  EAX, DWord Ptr ES:[SI.AIM_FKRate]     ;* AIM Values
      Mov  AIMRate, EAX                          ;* AIM for later use
      Mov  EAX, DWord Ptr ES:[SI.AIM_FKDelay]    ;* AIM by the Filter Key
      Mov  AIMDelay, EAX                         ;* AIM routine
      Pop  EAX                                   ;* AIM Restore reg
      CALLFAR TypaControl_Reset                  ;* AIM Reset the Filter Key Routine
      Or   AIMFlags, UnStickAll                  ;* AIM Set flag for resetting Sticky Keys
      CALLFAR UpdStickyTbl                       ;* AIM Reset the Sticky Key Routine

   .Elseif <bit ES:[SI.AIM_Active+1] z 0001h>
                                                 ;* AIM AIM support is being made inactive
      Call TypaControl_Disable                   ;* AIM Disable the Filter Key Routine
      Or   AIMFlags, UnStickAll                  ;* AIM Set flag for disabling Sticky Keys
      CALLFAR UpdStickyTbl                       ;* AIM Disable the Sticky Key Routine
      AND  AIMFLags, NOT AIMActive               ;* AIM Indicate that AIM is inactive

                                                 ;* AIM Begin Unlock AIMCode
     .If <bit AIMFlags nz AIMLocked>             ;* Aim If AIM is already locked
        Push    SI
        Push    AX
                                                 ;* AIM do a virttolin DevHelp (lockhandle)
        Mov     AX, DS
        Mov     ESI, offset LockHandle
        Mov     DL, DevHlp_VirtToLin
        Call    [DeviceHelp]
                                                 ;* AIM do a VMUnlock DevHelp using previously saved lock handle
        Mov     ESI, EAX                         ;* AIM get offset to lock handle
        Mov     DL, DevHlp_VMUnlock
        Call    [DeviceHelp]
        Pop     AX
        Pop     SI
     .Endif
                                                 ;* AIM End Unlock AIMCode
     .If <c>
         Jmp ExitGenFail
     .Endif
      AND AIMFlags, NOT AIMLocked                ;* AIM Clear flag if AIMCode segment unlocked.
   .Endif                                        ;* AIM Endif

.286p                                            ;* AIM Return to 80286 instruction set

 .Else                                           ;* AIM Else none of above cases were true

    LES BX, Dword Ptr StkRPO                     ;* AIM Restore request packet address.
    Mov ES:[BX+PktStatus],ParmError              ;* AIM Indicate invalid parameter in RP

 .Endif

ExitSGC:
  Mov  DI,StkPSG                                 ;* AIM Ensure DI points to caller's PSG.
  LES  BX,Dword Ptr StkRPO                       ;* AIM Restore request packet addr.
  Push 2222h                                     ;* AIM Var2 (Not used for this call)
  Push 1111h                                     ;* AIM Var1 (Not used for this call)
  Push DD_Cmd_Enable                             ;* AIM Function
  Call DD_Entry                              ;* AIM Device Independent Entry Point
  Ret

ExitGenFail:
  LES BX, Dword Ptr StkRPO                       ;* AIM Restore request packet address.
  Mov ES:[BX+PktStatus],GenError                 ;* AIM Indicate invalid parameter in RP
  Jmp ExitSGC

SGController Endp


BREAK <Category 0Ah, Function 40h - Register Keystroke Monitor>
Public MonRegister
MonRegister Proc Far

;***************************************************************************
;*
;*  FUNCTION NAME  : MonRegister
;*
;*  DESCRIPTION    : Registers the Monitor passed by the caller of         *
;*                   the IOCTL.  If no monitor exists for the desired
;*                   Session then DevHlp is called to create a Monitor
;*                   Chain.  The Monitor is then registered with the
;*                   Monitor Dispatcher (again, via DevHlp).
;*
;*                   A General Error code will eventually be returned
;*                   if the desired session is not within the current
;*                   range, or if the Register or Monitor Create failed.
;*
;*  FUNCTION       : See Description above.
;*
;*  NOTE           : A detached monitor will enter here without certain
;*                   stack variables defined, specifically, StkPSG, StkKCBO
;*                   and StkKCBS.
;*
;*
;*  INPUT          :
;*                     REGISTERS:
;*
;*                       ES:BX = Ptr to Request Block
;*
;*  EXIT-NORMAL    : Return to main Strategy processing.  The IOCTL
;*                   caller's data area is modified.
;*
;*  EXIT-ERROR     :
;*                  If a detached process wants to register a monitor
;*                  in a screen group greater than or equal to MaxSG OR
;*                  it wants to register a monitor in its own SG, then
;*                  the CmdError bit is set and the routine returns.
;*
;*                  If a un-detached process wants to register a monitor
;*                  in greater than or equal to MaxSG then the CmdError bit is
;*                  also set and the routine returns.
;*
;*                  If the caller tries to register a monitor for a PM
;*                  screen group we will return an ERROR_I24_NO_MONITOR_
;*                  SUPPORT. (DCR 274).
;*
;*  EFFECTS        : SI, AX, and DI are not preserved.
;*                   The Keystroke Monitor is registered with the Monitor
;*                   Dispatcher for the desired session.
;*
;*  INTERNAL REFERENCES:
;*     ROUTINES:
;*               IOCtlAddrCheck
;*
;*  EXTERNAL REFERENCES:
;*     ROUTINES:
;*               DevHlp (MonRegister, MonitorCreate)
;*
;***************************************************************************

  Push    CX                               
  Mov     CX,1                             ;* Set length of table for parm check.
  Mov     DH,0                             ;* Access parm = read only.
  Mov     SI,10                            ;* Set data buffer length parm.
  Call    IOCtlAddrCheck                   ;* Verify access to data buffer
  Pop     CX                               ;*
 .If < c >                                 ;* Carry set = access denied.
    Ret                                    ;* Get Out Now.
 .Endif                                    ;* End if access denied.
                                           

  Push    ES                               ;* Save ES for now.
  LES     SI,ES:[BX+DataBfrPtr]            ;* Get address of data buffer.
  Mov     AX,ES:[SI+2]                     ;* Get index from request data.


                                           
  Push    DX                               ;* save DX we will use it here
  Mov     DX,Word Ptr [LInfoSeg +2]        ;* establish addressibility
  Mov     ES,DX                            ;* to the Local InfoSegment
                                           
  Push    DI                               ;* save DI cause we use it w/ LIS
  Mov     DI,Word Ptr [LInfoSeg]           ;* get offset of LIS
  Mov     DH,Byte Ptr ES:[DI].LIS_ProcType ;* obtain LIS Process Type
 .If < DH  e  LIS_PT_DETACHED >            ;* If process is running detached
   .If < AX  ge  MaxSG > OR                ;* If requesting a session at or
                                           ;* above the MaxSG, OR IF requesting
   .If < Ax  e  -1 >                       ;* the to register in the current
        Pop    DI                          ;* session then..
        Pop    DX                          ;* restore the registers
        Pop    ES                          ;*  and the segment
        LES    BX, Dword Ptr StkRPO        ;* Restore request packet address.
        Mov    ES:[BX+PktStatus],CmdError  ;* Indicate invalid parameter in RP
        Ret                                ;*
   .EndIf                                  ;* EndIf not ae MaxSG OR curr. ses.
 .Else                                     ;* Else process is NOT detached
   .If < AX  ge  MaxSG >                   ;* If requesting at or above MaxSG
        Pop    DI                          ;* then..
        Pop    DX                          ;*
        Pop    ES                          ;*  restore registers and segment
        LES    BX, Dword Ptr StkRPO        ;* Restore request packet address.
        Mov    ES:[BX+PktStatus],CmdError  ;* Indicate invalid parameter in RP
        Ret                                ;*
   .EndIf                                  ;* EndIf request ae MaxSG
 .EndIf                                    ;* EndIf checking detached
  Pop    DI                                ;* restore DI
  Pop    DX                                ;* restore the register
 .If < AX  ne  -1 >                        ;* If NOT requesting current session
    Mov    DI,AX                           ;* Put index in an index reg.
    Shl    DI,1                            ;* Make it an offset.
    Mov    DI,[DI + PSGPointers]           ;* Point to SG of index.
 .EndIf                                    ;* EndIf NOT requesting curr. ses.
                                           
                                           ;*
                                           
  .If <bit [DI].PSGFlags nz SQMODE> OR     ;* SG is in SingleQ mode (or)
  .If <[DI].PSGNum eq Dos3XBoxSG>          ;* SG is a VDM
     Pop   ES                              ;* restore req packet seg/sel
     Mov   word ptr ES:[BX+PktStatus],MonError  ;* ERROR_I24_NO_MONITOR_SUPPORT
     Ret                                   ;* get out now
  .Endif                                   

  Mov   AX,[DI].MonHandle                  ;* Get handle for requested SG.
  Or    AX,AX                              ;* Check if monitor chain created.
 .If <z>                                   ;* Is one?
    Push DS                                ;* No, so save DS for now.
    Push DI                                ;* Save DI also.
    Mov  SI,DI                             ;* Move PSG pointer to SI.
    Add  SI,MonChainEnd                    ;* Set offset of end-of-chain buffer for this SG.
    Mov  DS:[SI],Word Ptr KeyPacketLen+2   ;* Set length of buffer.
    Mov  DI,Offset KbdCode:MonDispEntry    ;* Set notify routine offset.
    Push DS                                ;* Set segment for...
    Pop  ES                                ;*...end-of-chain buffer.
    Push AX                                ;* Save AX
    Mov  AX, KbdCode                       ;* Get KbdCode segment and
    Mov  DS,AX                             ;* put in DS
    Pop  AX                                ;* Restore AX
    Mov  DL,DevHlp_MonitorCreate           ;* Set DevHlp function.
    Call ES:DeviceHelp                     ;* Go create monitor chain.
    Pop  DI                                ;* Restore DI.
    Pop  DS                                ;* Restore DS.
   .If <c>                                 ;* Check if create succeded.
      Pop    ES                            ;* Get original request block segment.
      LES    BX, Dword Ptr StkRPO          ;* Restore request packet address.
      Mov    ES:[BX+PktStatus],GenError    ;* Indicate invalid parameter in RP
      Ret                                  ;* Get Out Now.
   .Endif                                  ;* Else okay, so set handle.
    Mov [DI].MonHandle,AX                  ;* Save handle in PSG.
 .Endif                                    ;* Else okay to register monitor.
  Pop ES                                   ;* restore caller's ES.
                                           
  Push   ES                                ;* Save ES again.
  LES    SI,ES:[BX+DataBfrPtr]             ;* Restore address of data buffer.
  Mov    CX, StkPID                        ;* Get caller's Process ID.
  Mov    DH,ES:[SI]                        ;* Get placement flag from data buffer.
  Push   DI                                ;* Save PSG address for now.
  Mov    DI,ES:[SI+8]                      ;* Get caller's output buffer offset from data bfr.
  LES    SI,ES:[SI+4]                      ;* Get caller's input buffer address from data bfr.
  Mov    DL,DevHlp_Register                ;* Set DevHlp function.
  Cli                                      ;* Don't allow ints while installing.
  Call   DeviceHelp                        ;* Go try to register it.
  Pop    DI                                ;* Restore PSG address.
  Pop    ES                                ;* Restore ES.
 .If <nc>                                  ;* Was register successful?
    Inc [DI].MonsInstalled                 ;* Yes, so increment monitor count for SG.
 .Else
    LES    BX, Dword Ptr StkRPO            ;* Restore request packet address.
    Mov    ES:[BX+PktStatus],GenError      ;* Indicate invalid parameter in RP
 .Endif
  Sti                                      ;* Allow ints now.
  Ret

MonRegister Endp


StratCode Ends
     End
