; SCCSID = src/dev/kbd/kbdbase.ssc/kbdmon.asm, kbd, c.basedd 98/01/16
        Page    58,132
        Title   KBDMON - Kbd Device Driver Monitor Code

;/*****************************************************************************
;*
;* SOURCE FILE NAME = KBDMON.ASM
;*
;* DESCRIPTIVE NAME = Physical Keyboard Device Driver Monitor Code
;*
;* COPYRIGHT    COPYRIGHT IBM CORPORATION, 1991, 1992
;*              Copyright Microsoft Corporation, 1990
;*              LICENSED MATERIAL - PROGRAM PROPERTY OF IBM
;*              REFER TO COPYRIGHT INSTRUCTION FORM#G120-2083
;*              RESTRICTED MATERIALS OF IBM
;*              IBM CONFIDENTIAL
;*
;* VERSION      V2.0
;*
;* DATE         04/15/93
;*
;* DESCRIPTION  This module contains Keyboard Monitor related code.
;*
;* FUNCTIONS    MonDispEntry()-Keystroke Monitor Dispatcher (Re)Entry Point
;*
;* NOTES        DEPENDENCIES:  Controller or keyboard must be set to the PC
;*                             compatible scan code set.
;*              RESTRICTIONS:  Machine must be a 386 or compatible with a 386.
;*
;*              ENTRY POINTS:  MonRegister
;*                             MonDispentry
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*
;*   10/09/92  @V2.0YEE01         D55711 - First character missing when
;*                                switching to/from DOS session with monitor
;*                                installed.
;*
;*   unknown   @V2.0xxx01         B712206 - Notification packet problem.
;*
;*   Unknown   @V2.0XXX02         B736552 - Unknown
;*
;*   Unknown   @V2.0XXX03         783515  - Unknown
;*
;*   04/12/94  81507              Defect 81507 - update sequence of instructions
;*                                for a Block call to meet specifications
;*
;*   UNKNOWN   J-DBCS001          DBCS enabling  (J-001)
;*   11/07/95  JS08511   PJ20939  IMMON isn't loaded in boot if shutdown with
;*                                OS/2 FS opened. (DCAF support)      (H.Mori)
;*   07/03/96  245j      PJ22907  Don't accept DosMonWrite if SG is not active
;*****************************************************************************/
     .286p
     .sall
     .xcref
     .xlist
     Include basemaca.inc    ;DOS macros.
     Include osmaca.inc      ;@IBM
     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 kbdseg.inc      ;Segment definitions.
     Include kbdddr.inc      ;Keyboard Device Driver structures & equates.
     .list

     ;External Routines
     Extrn AccessKCB         :Near
     Extrn HotKeyCheck       :Near
     Extrn PutInKIB          :Near
     Extrn ConsInpFilter     :Near
     Extrn PutInSQB          :Near               ;@IBM

     ;External Variables
     Extrn CBDataSel         :Word
     Extrn DeviceHelp        :DWord
     Extrn SQHotKeyData      :Byte               ;@V2.0YEE01

     StkFrameVars            ;Setup local stack varibles.

     Code Segment
     Assume  CS:Code,DS:DGROUP,ES:Nothing

     Public   MonDispEntry
     MonDispEntry Proc Far

;/***************************************************************************
;*
;* FUNCTION NAME = MonDispEntry()
;*
;* DESCRIPTION   = Keystroke Monitor Dispatcher (Re)Entry Point
;*
;*   This routine's address is passed to the monitor dispatcher, along
;*   with a End-of-Monitor Chain buffer address for each session for
;*   which a monitor chain exists.  When monitors are registered in a
;*   session, every keystroke is passed to the dispatcher.  This entry
;*   point is invoked after the monitor dispatcher has placed a packet
;*   in the End of Monitor Chain data area.
;*
;*   Note that once Key packets are passed to the monitor dispatcher it
;*   might change foreground sessions.  But monitors can still be
;*   passing characters when their session is not active.
;*
;*   When entered here, there is a record in the end-of-chain buffer
;*   whose address is in ES:SI at the time this routine is entered.
;*   Then check the CharData record in that buffer with the Console
;*   Input Filter code and throw out keys that are not allowed in a
;*   keyboard input buffer.  IF the CharData record remains,an attempt is made
;*   to acquire a KIB element from the FreeList.  IF not the foreground
;*   session, or IF already acquired half of the highest amount
;*   that has been available while active, I can't get any. In that
;*   case leave the record in the end-of-chain buffer and yield until
;*   it can get another record (IF in active session) or until the
;*   record has been read (background session).  Then return to the
;*   monitor dispatcher.
;*
;*   If Single Queue mode is in effect, then the Single Queue
;*   Device Driver Entry Point is called to place an SQDR into
;*   the SQB.  If the SQ device driver returns ERROR_SQ_NOT_
;*   IN_MODE, then the PSG will be taken out of SQ mode, and
;*   CharDataRecord will be placed into the KIB as usual.
;*
;*   Entry Point: MonDispentry     Linkage: FAR
;*
;*
;* INPUT         = A monitor packet which was placed in the output
;*                 buffer for the current screen group by the Monitor
;*                 Dispatcher.
;*
;* OUTPUT        = NONE(Keystroke buffers are altered, CharData flags are
;*                      updated).
;*
;* RETURN-NORMAL = Return to Monitor Dispatcher.
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

      Assume ES:DGROUP

       PushF                           ;Save flags.
       PushA                           ;Save regs.
       Mov     DI,SI                   ;Put buffer offset in DI.
       Sub     DI,MonChainEnd          ;Make DI point to beginning of bfr's PSG
       Add     SI,2                    ;Point past record length field in bfr.

  ;*
  ;*From here on DI is the index of the current PSG we're using and SI
  ;*is the index to the Key Packet. First check if this record is a
  ;*Flush record, indicating a flush is going on.
  ;*

  Test Byte Ptr [SI].KPacketLen,FlushPacket ;Check is this a flush packet.
 .If <nz> NEAR                              ;Was it?
ifdef DBCSKBD
    Push DI                                 ; Save PSG offset                     ;@IBM JS08511(A)
    Lea  BX, [DI].MonBlockID_F              ; Get ID that monitors may be blocked ;@IBM JS08511(A)
    Mov  AX, DS                             ;                                     ;@IBM JS08511(A)
    Mov  DL, DevHlp_ProcRun                 ; Set ProcRun DevHlp function.        ;@IBM JS08511(A)
    Call [DeviceHelp]                       ; Go run any blocked monitor threads  ;@IBM JS08511(A)
    Pop DI                                  ; Restore PSG offset                  ;@IBM JS08511(A)
endif ;DBCSKBD
    And [DI].PSGFlags,Not Flushing          ;Yes, indicate done.
 .Else NEAR                                 ;Check if we should ignore this
                                            ;packet.
                                                                        ;@V2.0xxx01
                                            ;Notification Packet. If so

  .If <<Byte Ptr [SI].DDFlags> eq XRorPNot > OR
   .If <<Byte Ptr [SI].DDFlags> eq MLNotification>

                                            ;DCR 408 Begin              ;@IBM
                                            ;Notification Packet, or
                                            ;If this is a Multi-Layer NLS
                                            ;Notification Packet
                                            ;IGNORE THIS PACKET!, also:
    And [DI].NotifyFlags, NOT NotPctSent    ;Clear Mons. did not accept flag.
                                            ;This is set due to XRorPNot packet
                                            ;NOTE: MLNotification packet donot
                                            ;set the NotPctSent flag.
                                            ;DCR 408 End                ;@IBM
                                            ;@V2.0XXX01
   .Else NEAR
ifdef DBCSKBD
      Mov AX,[SI].DDFlags                      ;If focus change or      ;@IBM J-DBCS001(A)
      AND AX,KeyTypeMask                       ;code page change packet ;@IBM J-DBCS001(A)
     .If <AX eq FocusChg> OR                   ;then only return        ;@IBM J-DBCS001(A)
     .If <AX eq CPChg>                                                  ;@IBM J-DBCS001(A)
       Jmp PacketProcessed                     ;If status change        ;@IBM J-DBCS001(A)
     .Elseif < AX eq Statuschg>                ;then clear waiting flag ;@IBM J-DBCS001(A)
       And [DI].PSGJFlags, Not Changing                                 ;@IBM J-DBCS001(A)
       Jmp PacketProcessed                                              ;@IBM J-DBCS001(A)
     .Endif                                                             ;@IBM J-DBCS001(A)
endif ;DBCSKBD

      Test Byte Ptr [SI].KPacketLen,OpenPacket+ClosePacket

     .If <z> NEAR AND                       ;OK to continue if not Open or
                                            ;Close packet.
      Test [DI].PSGFlags,Flushing           ;Check if flush in progress.
     .If <z> NEAR                           ;If not, process this packet.
                                            ;Inserted by monitor?
       Test Byte Ptr [SI].KPacketLen,NotInserted
       .If <z>                              ;Was it?
          Mov DH,MHotKeyDown                ;Set bit needed by next call.
          Mov AX,[SI].Key.Shift             ;Get shift status from key packet
                                            ;PTM 1426 (never was fixed) ;@IBM
                                            ;shift state in AX not BX.
          Call HotKeyCheck                  ;Yes so go check for SM hotkey.
       .Endif
                                                                        ;@V2.0XXX02
                                                                        ;@V2.0XXX02
        Test [DI].PSGFlags, SQMODE          ;Check for Single Queue mode.
       .If < nz > AND                       ;If we're in SQ mode, AND
        Test [SI].DDFlags, NotSQPacket      ;(Check if packet is to go in SQ)
       .If < z >                            ;Packet should go in SQB,
       Push    SI                           ;copy from kbdscan.asm when ;@V2.0YEE01
       Push    BX                           ;no monitor installed.      ;@V2.0YEE01

         .If <bit SQHotKeyData nz 03>       ;If nonzero                 ;@V2.0YEE01
                                                                        ;@V2.0YEE01
            Call PutInSQB                   ;Call SingleQ DD with Hot Key Info

         .Endif                             ;Endif                      ;@V2.0YEE01

       Pop     BX                                                       ;@V2.0YEE01
       Pop     SI                                                       ;@V2.0YEE01
       Call    PutInSQB                     ;Attempt to put an SQDR into
                                            ;the Single Queue Buffer.  This
                                            ;routine could clear SQ mode.
       .Endif                               ;End if in Single Queue Mode and
                                                                        ;@V2.0XXX02


        Call ConsInpFilter                  ;Go send events as appropriate.

       .If <nc> NEAR AND                    ;If ConsInpFilter said keep record
        Test [DI].PSGFlags, SQMODE          ;AND (check for Single Queue Mode)
       .If < z >                            ;NOT currently in SQ mode,
          Call PutInKIB                     ;Go add element to KIB.
          Clc                               ;Mov AccessKCB into loop    ;@V2.0XXX03

          Jmp short GetKCB                  ;and force skip of signal test.
                                            ;PTM 5625 Change while below from if ;@IBM
          Cli                               ;81507 - Disable interrupts
ifndef DBCSKBD	
         .While <ES:[BX].KCBInPtr eq 0>     ;Wait if rec still in EndOfChainBfr.
else	
         .While <ES:[BX].KCBInPtr eq 0> AND ;Wait if rec still in EndOfChainBfr.     ;@IBM J-245(A)
         .While <[DI].PSGFlags ne 0>        ;Check if this session is really active. ;@IBM J-245(A)
endif	
             Push AX                        ;81507 - Save ax
             Push BX                        ;Save KCB offset
             Push DI                        ;Save PSG offset
             Lea BX,[DI].MonBlockID         ;Get Blocker value...
             Mov AX,DS                      ;for use as Blocked Event
             Mov CX,-1                      ;Indicate to never timeout
             Mov DI,CX                      ;indicate to never timeout.(DI:CX)
             Mov DX, DevHlp_ProcBlock       ;DH = 0 = Interruptible block,
                                            ;DL = DevHlp function.
             Call [DeviceHelp]              ;Go block the monitor chain
             Cli                            ;81507 - Disable interrupts
             Pop DI                         ;Restore PSG offset
             Pop BX                         ;Restore KCB offset
             Pop AX                         ;81507 - Restore ax
GetKCB:                                                                 ;@V2.0XXX03
             Pushf                          ;Save the flags from the block.
             Mov DH,ES_DI                   ;Tell PhysToVirt where to put result
             Push DI                        ;Save PSG pointer
             Call AccessKCB                 ;Go get access to this SG's KCB
             Mov BX,DI                      ;Put KCB offset in BX
             Pop DI                         ;Restore PSG pointer
             Popf
; tmp JC     POPAF <AX>                     ;Macro to safely restore flags.
                                                                        ;@V2.0XXX03
ifdef DBCSKBD
             Cli                            ;Disable interrupts         ;@IBM J-245(A)
endif ;DBCSKBD
            .If <c> AND                     ;PTM 4629 - If woken by     ;@IBM
            .If <nz>                        ;Ctl-Break/C
                Mov SI,[DI].E0FwdPtr        ;Get E0's fwd pointer
                Mov ES:[BX].KCBInPtr,SI     ;Put it in KCB In Pointer
               .If <SI gt 0>                ;If there are other els in KIB
                   Mov ES,CBDataSel         ;Set ES KIB selector
                   Dec SI                   ;Correct for list starting w/ ele 1
                   IMul SI,KIBElLen         ;Set offset into element list.
                   Add SI,FLISTOFFSET       ;Point to the element.
                   Mov ES:[SI].KIBRevPtr,AX ;Set RevPtr to EOL.
               .Endif                       ;Endif other els in KIB
ifdef DBCSKBD
               Sti                          ;Enable interrupts          ;@IBM J-245(A)
endif ;DBCSKBD
               Jmp PacketProcessed          ;PTM 5625                   ;@IBM
            .Endif                          ;PTM 4629End if interrupted ;@IBM
         .EndWhile
          Sti                               ;Enable interrupts after PhysToVirt
       .Endif                               ;End "ConsInpFilter didn't eat key"
                                            ;And Not in Single Queue Mode.
     .Endif
   .Endif
 .Endif
PacketProcessed:
  PopA                                      ;Restore regs.
  PopF                                      ;Restore flags.
  Ret                                       ;Return to monitor dispatcher.

MonDispEntry Endp

Code Ends
     End
