;*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   KBDSUBS - Kbd Device Driver Common Subroutines
     Name    KBDSUBS

;/*************************************************************************
;*
;* SOURCE FILE NAME = KBDSUBS.ASM
;*
;* DESCRIPTIVE NAME = Kbd Device Driver Common Subroutines
;*
;*
;* VERSION      V2.0
;*
;* DATE         15/03/92
;*
;* DESCRIPTION  Hardware independant subroutines.
;*
;*              This file contains routines called by the other Keyboard Device
;*              Driver components.  They are broken out into this file merely to
;*              make the sizes of the other files more manageable, and to facil-
;*              itate multi-screen editing (e.g., look at the subroutine in one
;*              place while editing the caller in another).
;*
;* NOTES        DEPENDENCIES:  Controller or keyboard must be set to the PC compati
;*                             ble scan code set.
;*              RESTRICTIONS:  Machine must be a 386 or compatible with a 386.
;*
;*
;* FUNCTIONS    SetLocalVar()
;*
;*              SetPSGKCB  ()    Initialize the PSG and KCB stack variables.
;*
;*              CheckDetached()  request is for a keyboard or a monitor.
;*
;*              MonBlockCheck()  Block the processor if not the active session
;*                               and not interrupt time
;*
;*              PutInKIB()       Insert CharDataRec into KIB Free Element, if
;*                               possible.
;*
;*              PutInSQB()
;*
;*              DiscardElement() Remove KIB Element
;*
;*              PurgeKIB         Purge the Keystroke Input Buffer for a given
;*                               session.
;*
;*              AccessKCB        Access Current KCB for a Session
;*
;*              FreeKCB          Free KCB for a Session/PID
;*
;*              Unlock_CustomCP  Check if KCB is using a custom translate table
;*
;*              CheckTOEventID   Run a thread blocked waiting on an interrupt,
;*                               if necessary.
;*
;*              BlockCheck       Serialize access to the KIB, block if threads
;*                               ahead.
;*              UnBlockCheck     Clear serialization semaphore.
;*
;*              PutInVDMQ        Put scan code in VDM session Queue
;*
;*              HotKeyCheck      Check for Session Manager Hot Key with correct
;*                               Shift State.
;*              VDMPreXlate      Pre keystroke translation for a VDM scan cod
;*
;*              SendVDDSC        Send the scan code to the VDD
;*
;*              Update_SG_SS     Update the shift state for the screen group
;*
;*              Send_SScans      Send the shift key scans
;*
;*              VDMScanFilter    Filter out scans that have certain meaning
;*
;*              Layer_HKChk      Set proper index for switching between the
;*                               Primary and Secondary Layers.
;*              Typa_RDCheck     Check the rate and delay values and put
;*                               them into the appropriate format for the keyboard.
;*              KHBuffer         Keep a buffer of scan code MAKES for which BREAKS
;*                               have not yet occurred.
;*              SGActivation     Checks for and sets the bits in the SGCreatedTbl
;*
;*
;* STRUCTURES                    NONE
;*
;* EXTERNAL REFERENCES           NONE
;*
;*
;* EXTERNAL FUNCTIONS            NONE
;*
;*
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   11/20/92              57421   Scan code 0x80 caused infinite loop
;*                         PTM4990
;*                         DCR1131
;*                         B787292
;*                         B786969
;*                         B730591
;*                         B732177
;*                         DCR 42
;*                         PTM2189
;*                         PTM3479
;*
;*   2/13/87               PTM 1630 Allow any Close request to be valid.
;*                                  (Also changed order of interrogation) pjr
;*
;*   3/13/87               PTM 3235 Allow any Open request to be valid. pjr
;*
;*                         PTM 3080
;*                         PTM 6810
;*                         789221
;*                         709460
;*                         B733926  VMFree of the linear address
;*                         DCR 1187
;*
;*   7/08/94   86188   Change Team  Save/Restore ES in update_sg_ss
;*
;*
;**************************************************************************



.286p
.sall
.xcref
.xlist
  Include mvdm.inc
  Include osmaca.inc                   
  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 kbdaim.inc                   ; Keyboard Device Driver structures & equates.
  include vkbdpdd.inc                  ; Common structures and data shared between V/P(KBD)
.list

  StkFrameVars                         ; Define local storage stack variables.

;External Routines
  Extrn KbdXlate         :Near
  Extrn ProcessScanCode  :Near

;External Variables
  Extrn LockupFlags      :Byte         ;          
  Extrn MiscFlags        :Byte
  Extrn SQComp           :Byte
  Extrn HotKeyTable      :Byte
  Extrn HotKeyCount      :Byte
  Extrn VDMKeyQ          :Byte
  Extrn VDMQInPtr        :Byte
  Extrn OtherFlags       :Byte
  Extrn IntFlags         :Byte         ;          
  Extrn MiscFlags3       :Byte
  Extrn TypaRates        :Byte
  Extrn ShiftScanTable   :Word
  Extrn IntFlagArea      :Word
  Extrn SInfoSeg         :Word
  Extrn CurSG            :Word
  Extrn PSGPointers      :Word
  Extrn CBDataSel        :Word
  Extrn PDDtoVDDSCPkt    :Word
  Extrn MaxSG            :Word
  Extrn FoundHKID        :Word
  Extrn SQDDpds          :Word
  Extrn SQDDpm@          :Dword
  Extrn LInfoSeg         :Dword
  Extrn DeviceHelp       :DWord
  Extrn KCBSave          :Dword
  Extrn VDDEntryPoint    :fword
  Extrn SCCount          :Byte         ;           
  Extrn KeyHistBuf       :Byte         ;           
  Extrn SGCreatedTbl     :Byte
  Extrn AIMFlags         :Word         
  Extrn ShiftData        :Word         
  Extrn HKBypass         :Dword
  Extrn BypassTbl        :Byte
  Extrn VDMFocus         :Byte
  Extrn HKTemp           :Byte
  Extrn SQHotKeyData     :Byte         ;            
  Extrn NumPadAccum      :Byte         ;            


Code Segment

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

BREAK <SetLocalVar routine>

;/***************************************************************************
;*
;* FUNCTION NAME = SetLocalVar
;*
;* DESCRIPTION   = Set up local variables on device driver's stack
;*                 with necessary information.
;*
;*                 This routine is called from the main routine of either the
;*                 interrupt handling or strategy portion of the keyboard device
;*                 driver.  Upon entry to this routine, local storage has been
;*                 allocated on the stack, and specific EQUates have been performed
;*                 (via the LocalVar macro) to provide addressability to this
;*                 storage.  This routine places the necessary variables on the
;*                 stack so that the calling routine can use segment registers more
;*                 freely without an excessive number of pushes and pops.  The
;*                 variables which are placed on the stack depend upon whether
;*                 the interrupt handler or the strategy routine is the caller.
;*
;*
;* INPUT         = AX:  Sign bit signifies the caller:
;*
;*                        Sign bit = 1 indicates Interrupt Handler is calling.
;*                              DS = Device Driver Data Segment.
;*
;*                        Sign bit = 0 indicates Strategy routine is the caller.
;*                              DS = Device driver's data segment selector.
;*                           ES:BX = Sel:Off of Strategy Request Packet.
;*
;*
;*
;*
;* OUTPUT        =  Local Stack Frame is initialized.  DI set to PSG offset
;*                  as described in Exit-normal. AX set as described above.
;*
;* RETURN-NORMAL =  DI = PSG offset to be used by the caller (either the
;*                       active session if in the interrupt handler, or
;*                       the Strategy caller's session if at task time).
;*                  AX = Active session (if caller is interrupt handler),
;*                       or Strategy caller's session.
;*                  DX is altered.
;*
;*
;* RETURN-ERROR  =  Carry flag is set, if a detached process is detected
;*                  during a task time call.
;*
;*                  The CmdError bit is set if a detached process wants
;*                  to perform keyboard functions. It is also called if
;*                  a process with session above MaxSG attempts to do
;*                  kbd functoins. A return is then issued.
;*
;*
;* Internal Calls:
;*              SetPSGKCB               CheckDetached
;*
;*
;**************************************************************************/


Public SetLocalVar
SetLocalVar     Proc                   ; Set up local variables on the stack.

  Mov StkDS, DS                        ; Save the Data Segment regardless of
                                       ;  who is the caller.
  Test AX, AX                          ; See which portion of the DD called.
 .If < s >                             ; Sign bit on indicates the interrupt
                                       ;  handler is the caller.
    Mov AX, CurSG                      ; Get the current session for use in
                                       ;  calculating the PSG/KCB to use.
    Call SetPSGKCB                     ; Go set PSG and KCB addr.'s.

 .Else                                 ; Sign bit off indicates that the
                                       ;  caller was Strategy.
    Mov StkRPS, ES                     ; Save the Request Packet segment.
    Mov StkRPO, BX                     ; Save the Request Packet offset.


    ;*
    ;* NOTE:  If the strategy request of which this is a part is the
    ;*        the INIT command, then it is the KbdInit routine's
    ;*        responsibility to set the stack frame variable StkCBData
    ;*        correctly:
    ;*

    Mov AX,Word Ptr [LInfoSeg+2]       ; Get Local InfoSeg segment.
    Or  AX,AX                          ; Check if set yet.
   .If <nz>                            ; Is it?


        Push BX                          ; save BX, we will use it here
        Push ES                          ; Save the request packet sel.
        Mov  ES,AX                       ; Set segment.
        Mov  DI,Word Ptr [LInfoSeg]      ; Get offset of LInfoSeg.
        Mov  AX,ES:[DI].LIS_CurProcID    ; Get the caller's Process ID.
        Mov  StkPID, AX                  ; Save the PID on the stack.
        Mov  AX,ES:[DI].LIS_CurScrnGrp   ; Get the thread's session.
        Mov  BH,Byte Ptr ES:[DI].LIS_ProcType ; Get the thread's process type.
        Pop  ES                          ; Restore request packet sel.

      ;*
      ;*             The way process type is determined has been changed.
      ;*  The process type is now identified by a new field in the local
      ;*  information segment.  If this process type field contains a 4,
      ;*  then we know that the process is running detached and it is
      ;*  not allowed to execute any kbd functions.  The following check
      ;*  rejects detached keyboard processes.
      ;*

     .If < BH e LIS_PT_DETACHED>        ;               process type is
                                        ;  detached, check for ERROR.
        Pop BX                          ; restore the register BX.
        Call CheckDetached              ; Check detached request out.
       .If < c >                        ; Carry indicates request is
                                        ;  not allowed,
          LES BX, Dword Ptr StkRPO      ; Restore request packet address.
          Mov ES:[BX+PktStatus],CmdError; Indicate invalid parameter in RP
          Ret                           ; Return now.
       .Endif                           ; End if CheckDetached failed
                                        
     .Else                              ; Else not a detached process
       .If < AX ge MaxSG >              ; Check if request is from extended
                                        ; session, if so ....
          Mov AX,1                      ; then map request to session 1
       .Endif                           ; Endif request from extended session
                                        
        Pop BX                          ; Restore the register BX.
        Call SetPSGKCB                  ; Go set PSG and KCB addr.'s.
        Test MiscFlags3,IOCTl1st        ;            Do not flag this
       .If <nz> AND                     ; SG as in use until the first SG
                                        ; switch by a SetCurrentSG IOCTl.
       .If < AX e CurSG >               ;            AND do not flag as in use
                                        ; for the KBDD unless the strategy
         Or [DI].PSGFlags,SGInUse       ; request is from the foreground SG.
       .Endif                           ; Endif we have seen a SetCurrentSG
                                        ; AND CurSG matches thread SG.
                                        ;           
     .Endif                             ; End if detached process.
   .Else                                ; Else LInfoSeg not set yet.
      Mov StkPID, 0                     ; Ensure zero goes here.
   .Endif                               ; End if LInfoSeg set.
 .Endif                                 ; End check on who is the caller.

  Push AX                               ; Save the session number.
  Mov  AX, CBDataSel                    ; Get selector for KIB/CPCB memory.
  Mov  StkCBData, AX                    ; Put selector on the stack for either
                                        ;  caller.

  Pop AX                                ; Restore the session number.
  Clc                                   ; Indicate good processing.

  Ret                                   ; Stack set up complete.
SetLocalVar     Endp                    ; End SetLocalVar routine.


BREAK <SetPSGKCB Routine>


;/***************************************************************************
;*
;* FUNCTION NAME = SetPSGKCB
;*
;* DESCRIPTION   = Initialize the PSG and KCB stack variables.
;*
;*                 This routine is called by SetLocalVar to set up
;*                 the PSG and KCB variables in local storage (i.e.,
;*                 the stack).
;*
;* INPUT         = AX = Session number
;*
;* OUTPUT        = NONE
;*
;*
;* RETURN-NORMAL = DI = PSG address.
;*                 Stack variables are initialized:
;*                   - StkPSG
;*                   - StkKCBO
;*                   - StkKCBS
;*
;* RETURN-ERROR  = The carry flag is set.
;*
;*
;* EFFECTS       = DH is altered
;*
;*
;*
;**************************************************************************/


Public SetPSGKCB
SetPSGKCB Proc                              ; 

  Push    AX                                ; Save the session number.
 .If <AX ge sgidFirstVDM>                   ; If a VDM SGID
    Mov    DI,DOS3xBoxSG                    ; We will be using SG 2 for VDMs
 .Else                                      ; If this is NOT a VDM SG
    Mov    DI,AX                            ; use actual SG #
 .Endif
  Shl     DI,1                              ; Make it an offset.
  Mov     DI,[PSGPointers+DI]               ; Get PSG address for this SG.
  Mov     StkPSG, DI                        ; Save PSG offset on stack.
  Push    ES                                ; Save caller's ES reg.
  Mov     DH, ES_DI                         ; Specify regs. for next call.
  Call    AccessKCB                         ; ES:DI = seg/sel:off of KCB.
  Mov     StkKCBO, DI                       ; Put KCB offset on the stack.
  Mov     StkKCBS, ES                       ; Put KCB seg/sel on stack.
  Pop     ES                                ; Restore caller's reg.
  Mov     DI, StkPSG                        ; Restore the PSG offset.
  Pop     AX                                ; Restore the session number.

  Ret
SetPSGKCB Endp


BREAK <CheckDetached Routine>
Public CheckDetached
CheckDetached Proc

;/***************************************************************************
;*
;* FUNCTION NAME =  CheckDetached
;*
;* DESCRIPTION   =  Check detached process to determine whether
;*                  the request is for a keyboard or a monitor.
;*
;*                  Checks to see if a detached process is
;*                  performing a monitor request or a keyboard request.
;*                  Monitor requests (i.e., opens, closes, Monitor
;*                  Generic IOCTLs, and Monitor queries) are acceptable,
;*                  as well as a keyboard close request (ptm 1630).  Any
;*                  other keyboard requests are rejected.
;*
;* INPUT         =  REGISTERS:
;*
;*                    ES:BX = Ptr to Request Packet
;*                       DL = Request Packet request ID.
;*
;* OUTPUT        =  NONE
;*
;*
;* RETURN-NORMAL =  The carry flag is cleared if the request is determined
;*                  to be valid; otherwise, the carry flag is set,
;*                  indicating that the request is to be rejected.
;*
;*
;* RETURN-ERROR  =  NONE
;*
;*
;**************************************************************************/





  ;* Check the detached process for various cases...

 .If < DL e 10h >                       ; If req. is for Generic IOCTL,
   .If < ES:[BX+GIOCategory] e 4 >      ; If it's a generic KBD IOCTL,
     .If < ES:[BX+GIOFunction] e 059h > ;            If it's 59h let it
         Clc                            ; pass for a detached process.
     .Else                              ; If it is any other generic,
         Stc                            ;  detached KBD IOCTL ILLEGAL.
     .Endif                             ;           
   .Else                                ; Else not KBD generic IOCTL,
      Clc                               ;  let it PASS for now.
   .Endif                               ; End if KBD IOCTL check.
 .ElseIf < DL e 0Eh >                   ; If it's a close request,
    Clc                                 ; Any kbd/monitor close OK.
 .ElseIf < DL e 0Dh >                   ; If request is for an open,
    Clc                                 ; Any kbd/monitor open OK.
 .Else                                  ; Not open/close/Generic IOCTL
    Stc                                 ; All other requests FAIL.
 .Endif                                 ; End detached process cases.

  Ret

CheckDetached Endp



                            

BREAK <MonBlockCheck subroutine>
Public MonBlockCheck
MonBlockCheck Proc
;/***************************************************************************
;*
;* FUNCTION NAME = MonBlockCheck (previously YieldCheck -            
;*
;* DESCRIPTION   = Block the processor if not the active session
;*                       and not interrupt time.
;*
;*                  Called by the Console Input Filter to ensure that
;*                  a high priority thread gets blocked.  This routine
;*                  was formerly YieldCheck, which yielded the processor
;*                  as long as the session in which the thread resided
;*                  was not the active session.  In PTM 6810 the routine
;*                  was renamed and recoded to insure that a high priorty
;*                  thread at least gets blocked instead of constantly
;*                  being yielded on.
;*
;* INPUT         =  DI = Ptr to Current PSDA
;*
;*
;*
;*
;*
;*
;* OUTPUT        =  NONE
;*
;*
;* RETURN-NORMAL =  Return to caller of routine.
;*
;* RETURN-ERROR  =  None.  All exits are through NORMAL above.
;*
;*
;**************************************************************************/


                                          ;           
  Cli                                     ; Disable int's for Test
  Test  [DI].PSGFlags,ActiveSG            ; Check if in active SG and
 .While <z> AND                           ; If it is not interrupt time
     Test [OtherFlags],InterruptTime      ; then Block the thread.
    .While <z>                            ; 
       Mov  AX,DS                         ; assign MonEventID the
       Mov  BX,DI                         ; segment and its offset.
       Add  BX,MonEventID                 ; 
       Mov  Word Ptr [DI].MonEventID,AX   ; 
       Mov  Word Ptr [DI].MonEventID+2,BX ; 
       Push DX
       Push DI                            ; 
       Push CX                            ; 
       Mov  BX,Word Ptr [DI].MonEventID   ; move in low word of MonEventID.
       Mov  AX,Word Ptr [DI].MonEventID+2 ; move in high word of MonEventID.
       Mov  CX,-1                         ; timeout interval is -1 means
       Mov  DI,CX                         ; Block waits until ProcRun.
       Xor  DH,DH                         ; sleep is non-interruptible.
       Mov  DL,DevHlp_ProcBlock           ; set Proc_Block function.
       Call [DeviceHelp]                  ; go do it.
       Pop  CX                            ; 
       Pop  DI                            ; restore DI.
       Pop  DX
       Cli                                ; 
       Test [DI].PSGFlags,ActiveSG        ; Check if in active SG.
 .EndWhile                                ; 
  Sti                                     ; 
  Ret                                     

MonBlockCheck Endp


BREAK <PutInKIB subroutine>
Public PutInKIB
PutInKIB   Proc

;/***************************************************************************
;*
;* FUNCTION NAME = PutInKIB
;*
;* DESCRIPTION   = Insert CharDataRec into KIB Free Element, if
;*                 possible.
;*
;*
;*                 If the number of KIB elements for the session is less
;*                 than 1/2 of the maximum number of free elements
;*                 available, then the CharDataRec in the PSDA End of
;*                 Monitor Chain Buffer is copied into the next
;*                 available free list entry, and the various link
;*                 pointers in the KIB and current session's KCB  are
;*                 updated accordingly.
;*
;*                 If there is a thread blocked waiting for characters,
;*                 then this routine runs the thread after inserting
;*                 the CharData records in the KIB.
;*
;*
;* INPUT         = DI = Current PSG        SI = Current End-of-mon-chain bfr.
;*
;* OUTPUT        = Altered:  AX,BX,CX,DX
;*
;*                 The KIB Freelist is possibly reduced. The KIB for the Session
;*                 is increased.  The PSDA is updated.
;*
;* RETURN-NORMAL = Return to caller of routine.
;*
;* RETURN-ERROR  = None.  All exits are through NORMAL above.
;*
;*
;*
;**************************************************************************/


  ;*
  ;* On Entry: DI = Current PSG,  SI = Current End-of-mon-chain bfr.
  ;* Altered:  AX,BX,CX,DX,ES
  ;*

        test    [di].PSGFlags,Flushing  ;* prevent stomping on purge in progress
        jz      notfls
        ret
notfls:


       Push    SI                           ; Save SI for now.
       Add     SI,2                         ; Set SI to point to CharData record.
       Push    DI                           ; Save PSG ptr.
       Mov     ES,CBDataSel                 ; Get KIB selector
       Mov     BX,FLISTHEADER               ; ES:BX points to KIB header
       Mov     CX,ES:[BX].MaxThisTime       ; Get no. of elements avail.
       ShR     CX,1                         ; Can only get half of max while active.
       Mov     DH,ES_DI                     ; Tell AccessKCB where to put Virt. addr
       Call    AccessKCB                    ; Get acces to this SG's KCB
       Mov     BX,DI                        ; Put KCB offset in BX
       Pop     DI                           ; Restore the PSG pointer
       Push    DI                           ; Save the PSG, again!
       Mov     AX,ES:[BX].KCBInPtr          ; Get current KCB in-ptr.
       Push    AX                           ; Save the KCB in-ptr

       Test   [DI].PSGFlags,ActiveSG        ; Check if in active SG.
      .If <nz> AND                          ; Are we?
      .If <ES:[BX].KCBQueueCt b CX>         ; If don't have that many, get free element.
       Push    DI                           ; Save the PSG pointer
       Inc     ES:[BX].KCBQueueCt           ; Increment KIB length counter.
       Mov     ES,CBDataSel                 ; Get KIB selector
       Mov     BX,FLISTHEADER               ; ES:BX points to KIB header
       Mov     AX,ES:[BX].FreeListIn        ; Get freelist in-pointer index.
       Mov     DI,AX                        ; Put in destination index reg.
       Dec     DI                           ; Correct for list starting with element 1.
       IMul    DI,KIBElLen                  ; Set offset into element list.
       Add     DI,FLISTOFFSET               ; Point to the free element.
       Push    DI                           ; Save ptr for later.
       Mov     DX,ES:[DI].KIBFwdPtr         ; Get the forward ptr from that element.
       Mov     ES:[BX].FreeListIn,DX        ; Update free-list in-pointer.
       Dec     ES:[BX].ElsInList            ; Update free-list count.
       Mov     CX,ChDaRecLen                ; Set length of CharData rec for copy.
       Cld                                  ; Make sure we don't decrement!
       Rep     MovSB                        ; Copy the CharData rec.
       Pop     SI                           ; Leave SI pointing to this new element.
       Pop     DI                           ; Restore the PSG pointer
      .Else
        Xor AX,AX                       ; Clear AX (in case we've reached
                                        ;  end of free list) to set pointers.
      Push  DS                          ; Set ES to point to the
      Pop   ES                          ;  device driver's data segment
   .Endif
   Pop CX                               ; Restore the KCB in-ptr


        ;*
        ;* Now, if the element was added to the KIB, then ES:SI will point
        ;* to that element.  If the KIB was full, or this screen group is
        ;* not the active screen group, then ES:SI will point to the
        ;* End-of-Monitor-Chain buffer for that screen group.
        ;* CX contains the KCBInPtr for this SG's KIB.
        ;*

      Mov    ES:[SI].KIBFwdPtr,CX          ; Put in current element as FwdPtr.
      Mov    ES:[SI].KIBRevPtr,-1          ; Mark RevPtr as end of list.
     .If <CX eq -1>                        ; If no elements in list already
       Mov    DH,ES_DI                     ; Tell AccessKCB where to put Virt. addr
       Call   AccessKCB                    ; Get acces to this SG's KCB
       Mov    ES:[DI].KCBOutPtr,AX         ; Point OutPtr at new element.
       Mov    ES:[DI].KCBInPtr,AX          ; Point KIB InPtr at new element.
     .Else                                 ; Else go fix RevPtr of existing element.
       Mov    ES,CBDataSel                 ; Set ES KIB selector
       Mov    SI,CX                        ; Put KCBIntPtr in index reg.
       Dec    SI                           ; Correct for list starting with element 1.
       IMul   SI,KIBElLen                  ; Set offset into element list.
       Add    SI,FLISTOFFSET               ; Point to the element.
       Mov    ES:[SI].KIBRevPtr,AX         ; Point RevPtr at newly added element.
       Mov    DH,ES_DI                     ; Tell AccessKCB where to put Virt. addr
       Call   AccessKCB                    ; Get acces to this SG's KCB
       Mov    ES:[DI].KCBInPtr,AX          ; Point KIB InPtr at new element.
    .EndIf
    Pop   DI                               ; Restore PSG now
    Pop   SI                               ; Restore original SI now.
    Mov   AX,Word Ptr [DI].EventID+2       ; Get seg of possible Blocked EventID.
    Or    AX,AX                            ; Check if someone blocked waiting for Chars.
   .If <nz>                                ; Is there?
       Mov  BX,Word Ptr [DI].EventID       ; Yes, get offset of Blocked Event ID.
       Mov  DL,DevHlp_ProcRun              ; Set ProcRun DevHlp function.
       Call [DeviceHelp]                   ; Go wakeup the blocked process.
       Mov  Word Ptr [DI].EventID,0        ; Clear Blocked
       Mov  Word Ptr [DI].EventID+2,0      ; Event ID.
   .Endif
   Ret


PutInKIB Endp

BREAK <PutInSQB subroutine>
Public PutInSQB
Procedure PutInSQB, HYBRID


;/***************************************************************************
;*
;* FUNCTION NAME = PutInSQB
;*
;* DESCRIPTION   = Call the Single Queue device driver to write
;*                 a Single Queue Data Record (SQDR) to the Single
;*                 Queue Buffer (SQB).
;*
;*                 This routine tests if the current key packet is a
;*                 make scan code packet.  If it is, it tests if it
;*                 is the same make packet as the previous make packet.
;*                 If this is true then it performs the Update SQ
;*                 function.  If it is not the same make packet, it
;*                 performs the Write SQ function with a reserve count
;*                 for one break packet.  When a break comes in, it
;*                 performs the SQ Write function with a negative
;*                 reserve count to free the space (reserved on the make
;*                 for this break packet.
;*
;*                 If the SQDR returns ERROR_SQ_NOT_IN_MODE, then this
;*                 routine resets the PSG SQ mode indicator.
;*
;*
;* INPUT        =   REGISTERS:
;*
;*                    DS:SI = Ptr to Keystroke Packet
;*                    DS:DI = Ptr to PSG Data Area
;*
;* OUTPUT       =  SQDR transferred to SQB.
;*                 AX, BX, CX, ES registers altered.
;*
;*
;* RETURN-NORMAL = Return to caller.
;*
;* RETURN-ERROR  = None.
;*
;*
;**************************************************************************/


  ;*
  ;*  AH = caller is KBD DD,
  ;*  AL = Packet length, including header.
  ;*
                                        ;           
 .If <bit SQHotKeyData nz 3>            ; IF Hot Key Data need be sent, do it!
    Mov    AX, 0304h                    ; Set up data to send...
    Xor    BL, BL                       ; 
    Mov    SI, Offset HKBypass          ; 
   .If <bit SQHotKeyData nz 2>          ; If VDM not in Focus
.386p                                   ; 
      Mov HKBypass, 04                  ; Indicate to bypass Alt+Home Hot Key
.286p                                   ; 
   .Endif                               ; 
    Mov   SQHotKeyData, 0               ; 
    Jmp   CallTheSQ                     ; Make the call
                                        ;           
 .Else
    Mov   AX, 0100h + KeyPacketLen + Que_Header_Size
 .Endif

  Test [SI].DDFlags,KeyBreak            ; Check if this is break
 .If <nz>                               ; If this is a break scan code
    Mov BX, 0802h                       ; High byte = Offset to timestamp
                                        ;  in data packet, low byte =
                                        ;  write function code.
    Mov CX, KeyPacketLen + Que_Header_Size      ; CH = Reserve count = 0.
                                                ; CL = Free count = Size
                                                ;      of packet and hdr.

 .Else                                  ; Else its a make scan code
    Push  DI                            ; Save DI
    Mov   DI,Offset SQComp              ; Get offset of saved key packet
    Mov   CL,[DI].Key.Scan              ; Get saved Scan code
   .If <CL eq [SI].Key.Scan> AND        ; If this is equal to the current
                                        ;   scan code AND....
    Mov   CX,[DI].Key.Shift             ; Get saved shift state
   .If <CX eq [SI].Key.Shift> AND       ; If this is equal to the current
                                        ;  shift state AND...
    Mov   CL,[DI].Key.DShift            ; Get saved NLS Shift state
   .If <CL eq [SI].Key.DShift> AND      ; If this is equal to the current
                                        ;  NLS shift state AND...
    Mov   CX,[DI].DDFlags               ; Get saved DDflags
   .If <CX eq [SI].DDFlags>             ; If these are equal to current
                                        ;  DDflags
     Mov   BX,(Offset Key.Time SHL 8)+4 ; High byte = Offset to timestamp
                                        ;  in data packet, low byte =
                                        ;  update function code.
   .Else                                ; Else this make is not the same
                                        ; as the saved make key packet
       Push   SI                        ; Save key packet pointer.
       Mov    CX,DS                     ; Get DD's DS
       Mov    ES,CX                     ; Put it in Destination reg
       Mov    CX, KeyPacketLen          ; Set copy length
       Cld                              ; Make sure we don't decrement.
       Rep    MovSB                     ; Save new make packet
       Pop SI                           ; Restore key packet pointer.

       ;* CH = reserve count = packet plus header,
       ;* CL = free count = 0

       Mov    CX, (KeyPacketLen+Que_Header_Size) SHL 8
       Mov    BX, 0802h                 ; High byte = Offset to timestamp
                                        ;  in data packet, low byte =
                                        ;  write function code.

   .Endif                               ; Endif this is typamatic or not
    Pop DI                              ; Restore DI
 .Endif                                 ; Endif this was a key break or not


CallTheSQ:
  Mov ES, SQDDpds                       ; ES = SQDD prot mode data selector
  Call [SQDDpm@]                        ; Call the prot mode entry point
                                        ; of the SQ DD.

 .If <AX eq ERROR_SQ_INSUF_SPACE> AND   ; If there was no room for packet
  Test  [SI].DDFlags,KeyBreak           ; Check if this is break
 .If < z > AND                          ; Packet is NOT for a break, AND
  Test  [SI].Key.Status,1               ; Is it a shift report packet
 .If <nz> OR                            ; If it is OR
  Mov   DX,[SI].DDFlags                 ; Get DDFlags
  And   DX,ShiftMask                    ; Clear all bits except shift value
 .If <DX ne ShiftMask>                  ; If its not a shift key then...
    Or  [MiscFlags],BeepSpeaker         ; so indicate beep.
 .ElseIf <AX eq ERROR_SQ_NOT_IN_MODE>   ; Else if SQ DD is no longer in mode,
    And  [DI].PSGFlags,Not SQMODE       ; Turn off SQ mode for this session
 .Endif                                 ; Endif an error occured
  Ret
EndProc PutInSQB

BREAK <DiscardElement subroutine>
Public DiscardElement
Procedure DiscardElement, HYBRID

;/***************************************************************************
;*
;* FUNCTION NAME = DiscardElement
;*
;* DESCRIPTION   = Remove KIB Element.
;*
;*
;*                  This routine is called when a KIB element is no
;*                  longer required by the a given session.  The routine
;*                  will first check to see if the KIB for the active
;*                  session is full and it is unable to add an element
;*                  to its KIB (i.e., the number of KIB elements being
;*                  used by the active session is more than 1/2 the
;*                  number of free elements available in the KIB).  If
;*                  the active session could use the freed element, then
;*                  its Element 0 CharData record is copied to the
;*                  element to be freed, and the links are updated
;*                  accordingly.
;*
;*                  If the active session does not require the freed
;*                  element, then the element is added to the free list
;*                  and tracking variables in the free list's header
;*                  are updated as appropriate.
;*
;* INPUT        =   ES:DI=ptr to element to discard.    AX=index of that element.
;*                  DS:SI=Desired PSG of the caller.
;*
;* RETURN-NORMAL =  Return to caller of routine.
;*
;*
;* RETURN-ERROR  =  None.  All exits are through NORMAL above.
;*
;* OUTPUT        =  The KIB Freelist is possibly enlarged. Element 0 is possibly
;*                  moved to the KIB.  KIB variables (in the elements themselves,
;*                  the Free List Header, and the KCB for both the strategy caller's
;*                  and active session) are potentially updated.
;*
;**************************************************************************/



     Push CX                        ; Preserve CX
     Push DS                        ; Save Kbd DD Data Segment.
     Push SI                        ; Save PSG pointer.
     Push BX                        ; Save caller's register.
     Push DX                        ; Save caller's register.

     Mov  DS, StkDS                 ; Point to DD's data segment.

     Push SI                        ; Save SI=Threads PSG pointer
     Push DI                        ; Save DI=KIB offset of element to discard

     Mov  DI,SI                     ; DI= Thread's PSG pointer
     Mov  DH,DS_SI                  ; Tell AccessKCB where to put KCB
                                    ;  virtual address
     Call AccessKCB                 ; DS:SI=Tread's KCB
     Dec  [SI].KCBQueueCt           ; Indicate that element is gone.

     Mov  DS, StkDS                 ; Point to DD's data segment.
     Mov  DI, CurSG                 ; Get currently active SG number.
    .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,[PSGPointers+DI]       ; DI= PSG address for active SG.
     Mov  DH,DS_SI                  ; Tell AccessKCB where to put KCB
                                    ;  virtual address
     Call AccessKCB                 ; DS:SI = KCB for active SG
     Mov  DX,DI                     ; DX= PSG address for active SG.
     Pop  DI                        ; Restore DI=KIB offset of element to discard


     Mov  BX, KIBOFFSET             ; Point ES:BX to the KIB header.

    .If <[SI].KCBInPtr eq 0> AND    ; Is active session's KIB full?
     Mov  CX, ES:[BX].MaxThisTime   ; Yes, so check if elements avail.
     ShR  CX,1                      ; We can only get 1/2 max when active.
    .If <[SI].KCBQueueCt be CX>     ; If don't have that many, move element 0.
       Inc [SI].KCBQueueCt          ; Indicate KCB now has an additional el
                                    ; in the KIB proper.
       Mov [SI].KCBInPtr,AX         ; Make active KCB point to newest
                                    ;  element.
       Mov DS, StkDS                ; Point to DD's data segment.
       Mov BX, DX                   ; Get offset of active session's PSG.
       Mov DX,[BX].E0FwdPtr         ; Get old FwdPtr from element zero.
       Mov ES:[DI].KIBFwdPtr, DX    ; Put old el 0 FwdPtr in FwdPtr of the
                                    ;  just freed element.
       Mov ES:[DI].KIBRevPtr, -1    ; Mark the same element as the end of
                                    ;  the KIB.
       Mov SI, BX                   ; Put active session's PSG offset into
                                    ;  an index register.
       Add SI,ElementZero           ; Make source point to el to move.
       Mov CX,ChDaRecLen            ; Set length of copy.
       ClD                          ; Be sure we don't decrement.
       Rep MovSB                    ; Move the CharData rec.
      .If <DX ne -1>                ; If more than 1 el in the SGs list....
         Push AX                    ; Save the new element index.
         Mov  AX, DX                ; Put old E0FwdPtr in index reg.
         Dec  AX                    ; Correct for list starting with element 1.
         IMul AX,KIBElLen           ; Set offset into element list.
         Add  AX,FLISTOFFSET        ; Adjust for location of KIB in seg.
         Mov  DI, AX                ; Put the result in an index register.
         Pop  AX                    ; Restore the new element index.
         Mov  ES:[DI].KIBRevPtr, AX ; Make this element point to the
                                    ;  element just added to the KIB.
      .EndIf
       Pop SI                       ; Fixup stack SI=Thread's PSG pointer
    .Else                           ; Active session doesn't have a full
                                    ;  or it has room to add an element,
                                    ;  so put freed element into FreeList.
       Mov  DS, StkDS               ; Point to DD's data segment.
       Pop  SI                      ; SI=Thread's PSG pointer
       Push DI                      ; Save DI=KIB offset of element to discard
       Mov  DI,SI                   ; Put PSG pointer where AccessKCB expects it
       Mov  DH,DS_SI                ; Tell AccessKCB where to put KCB
                                    ;  virtual address
       Call AccessKCB               ; DS:SI = Thread's KCB address
       Pop  DI                      ; Restore DI=KIB offset of element to discard

       Mov  DX, ES:[BX].FreeListIn  ; Get pointer to first free element.
       Mov  ES:[BX].FreeListIn, AX  ; Replace with freed element index.
       Mov  ES:[DI].KIBFwdPtr, DX   ; Point newly freed element to the
                                    ;  rest of the free list.
       Mov  AX, ES:[BX].ElsInList   ; Get freelist count.
       Inc  AX                      ;Increment it.
       Mov  ES:[BX].ElsInList, AX          ; Store it.
      .If <AX a ES:[BX].MaxThisTime>       ; Is this a new high for SG?
         Mov ES:[BX].MaxThisTime, AX       ; Yes, so indicate so.
      .Endif
       ;*
       ;* Check the KCB's KIB Free list count.  If it is zero (meaning
       ;* there are no elements in the KIB), then ensure that the KCB
       ;* head/tail ptrs. are -1, indicating an empty KIB.
       ;*

      .If < [SI].KCBQueueCt eq 0 > AND ; If no elements in the KIB AND
      .If < [SI].KCBInPtr ne 0 >    ; and no element in E0 area (Janet)
         Mov [SI].KCBInPtr, -1      ; One element in KIB, mark the KCB
         Mov [SI].KCBOutPtr, -1     ;  as having NO elements in the KIB.
      .Endif                        ; End if one element in KIB.
    .Endif

     Sti
     Pop DX                         ; Restore caller's registers.
     Pop BX                         ;  "
     Pop SI                         ; Restore PSG pointer.
     Pop DS                         ; Restore KCB segment.
     Pop CX                         ; Restore CX
     Ret

EndProc DiscardElement

BREAK <PurgeKIB subroutine>
Public PurgeKIB
Procedure PurgeKIB, HYBRID

;/***************************************************************************
;*
;* FUNCTION NAME = PurgeKIB
;*
;* DESCRIPTION   = Purge the Keystroke Input Buffer for a given
;*                 session.
;*
;*
;*                 All KIB elements currently in the possession of the
;*                 particular session are added back to the KIB Free
;*                 List (note: the "element 0" entry is NOT given back)
;*
;*
;*
;* INPUT         = DI = KCB  Pointer of KIB to purge.
;*
;*                 PTR B789221 Introduced a check of the AX register:
;*                 AX = 0    Access the KCB
;*                    = 1    Do Not Access the KCB because DestroyKbd has adjusted
;*                           the KCB Pointers.
;*
;*
;*
;* OUTPUT        = The KIB Freelist is enlarged.  KIB variables are reset to
;*                 indicate no KIB for the session.
;*
;* RETURN-NORMAL =  Return to caller of routine.
;*
;* RETURN-ERROR  =  None.  All exits are through NORMAL above.
;*
;*
;**************************************************************************/

        push    ds
        push    es
        pusha
        cmp     ax,0                    ;should we access the KCB?
        jne     havkcb
        push    di                      ;Save PSG pointer for now
        mov     dh,ES_DI                ;Specify for register pair for
        call    AccessKCB               ;AccessKCB to use and call it
        mov     bx,di                   ;Mov KCB offset to BX
        pop     di                      ;Restore PSG pointer

havkcb: mov     dx,es:[bx].KCBInPtr     ;Get KIB InPtr index for this SG's KC
        or      dx,dx                   ;Check if pointing to element zero.
        jnz     havkib
        mov     dx,[di].E0FwdPtr        ;Yes, so get element 0's fwd ptr.
havkib: mov     ds,CBDataSel            ;Get the KIB Selector
        mov     di,FLISTHEADER          ;Get the Free List Header offset
        cmp     dx,-1
        je      kibemp                  ;if KIB already empty....

        mov     cx,[di].FreeListIn      ;Get index to freelist.
        mov     [di].FreeListIn,dx      ;Set new index to freelist.
        mov     ax,es:[bx].KCBOutPtr    ;Get index of other end of freed list
        dec     ax                      ;Correct for list starting with eleme
        imul    ax,KIBElLen             ;Set offset into element list.
        add     ax,FLISTOFFSET          ;Point to the element.
        mov     si,ax                   ;Put result in an index register.
        mov     [si].KIBFwdPtr,cx       ;Put old freelist InPtr there.

        xor     ax,ax
        dec     ax                      ;ax=-1
        mov     es:[bx].KCBInPtr,ax     ;Set indexes to...
        mov     es:[bx].KCBOutPtr,ax    ;...indicate empty.
        inc     ax                      ;ax=0
        xchg    ax,es:[bx].KCBQueueCt   ;Zero KIB Size, put old size in AX
        add     [di].ElsInList,ax       ;add to freelist count
        mov     cx,[di].ElsInList
        cmp     cx,[di].MaxThisTime     ;is this a new high?
        jle     kibemp
        mov     [di].MaxThisTime,cx     ;Yes, so indicate it!

kibemp: popa
        pop     es
        pop     ds
        ret

EndProc PurgeKIB



BREAK <AccessKCB subroutine>
Public AccessKCB
Procedure AccessKCB, HYBRID

;/***************************************************************************
;*
;* FUNCTION NAME = AccessKCB
;*
;* DESCRIPTION   = Access Current KCB for a Session
;*
;*                  This routine established addressability to a Sessions current
;*                  KCB.  It checks if the KCB physical address in the PSG data area
;*                  is 0.
;*                  If it is, that means the the current KCB for that SG is the
;*                  default KCB within the PSG data area.  If the KCB address in the
;*                  PSG data are is not 0,  then it does a PhysToVirt on the physica
;*                  address.
;*
;* INPUT         =  DI = PSG ptr to SG for whose KCB access is wanted
;*                  DH = desired destination registers (0=DS:SI, 1=ES:DI)
;*                  DS = Device Driver Data Seg
;*
;* OUTPUT        =  NONE
;*
;* NOTE          =
;*                 This routine can ONLY be called if the caller is assured that a
;*                 mode switch will NOT occur; otherwise, PhysToVirt restrictions
;*                 apply.
;*
;*                 See also the MAccessKCB macro in KBDDD.INC, which is used when
;*                 a possible PhysToVirt will occur where a routine cannot exit
;*                 to its caller (PhysToVirt restriction).
;*
;*
;* RETURN-NORMAL = Virtual address of KCB is in desired registers
;*
;* RETURN-ERROR  = None.
;*
;*
;**************************************************************************/


.If <<Word Ptr [DI].KCBAddress> ne 00h>  ; If KCB is not a default KCB
   Push AX                               ; save regs
   Push BX                               ; 
   Push CX                               ; 
  .If <DH eq ES_DI>                      ; If caller wants virt. addr. returned
      Push SI                            ; in ES_DI, then save SI.  Otherwise
  .Endif                                 ; he wants it in DS:SI and no use saving
   Push DX                               ; 
   Mov  BX,Word Ptr [DI].KCBAddress      ; Get the physical
   Mov  AX,Word Ptr [DI].KCBAddress+2    ;  address of the KCB.
   Mov  CX,KCBSIZE                       ; Get the KCB size
   Mov  DL,DevHlp_PhysToVirt             ; Specify Dev_Hlp Function
   Call [DeviceHelp]                     ; Call DevHlp
   Pop DX                                ; 
  .If <DH eq ES_DI>                      ; If caller wants virt. addr. returned
      Pop SI                             ; in ES_DI, then restore SI.  Otherwise
  .Endif                                 ; he wants it in DS:SI and we won't ovewrite.
   Pop CX                                ; 
   Pop BX                                ; 
   Pop AX                                ; 
.Else                                    ; Else KCB ptr was 0 (use DKCB)
  .If <DH eq DS_SI>                      ; If caller wants KCB virt addr in DS:SI
     Mov SI, DI                          ; Put the Default KCB offset in SI
     Add SI, DKCB                        ;  DS is already pointing to the Data seg
                                         ;  where Default KCBs are
  .Else                                  ; Else caller wants KCB virt addr. in ES:DI
     Push DS                             ; Move Data Segment selector
     Pop  ES                             ; into ES
     Add  DI, DKCB                       ; get the KCB offset for this SG
  .Endif
.Endif                                   ; Endif Default KCB check
 Ret
EndProc AccessKCB


BREAK <FreeKCB subroutine>
Public FreeKCB
Procedure FreeKCB, HYBRID

;/***************************************************************************
;*
;* FUNCTION NAME = FreeKCB
;*
;* DESCRIPTION   = Free KCB for a Session/PID
;*
;*                 First check to see if both the Previous and Next pointers in
;*                 the KCB are zero.  If so, then the KCB is the only one in
;*                 the list and the PSG start/end pointers should be changed to
;*                 indicate that the chain will now be empty.  Otherwise, the
;*                 KCB will be removed and the previous/next KCB's links will
;*                 be adjusted accordingly, as will the PSG start/end pointers,
;*                 if necessary.
;*
;* INPUT         =
;*                  ES:DI = Virtual address of KCB.
;*                  KCBSave = Physical address of KCB.
;*
;* OUTPUT        =  NONE
;*
;*
;* RETURN-NORMAL =  NONE
;*
;*
;* RETURN-ERROR  =  NONE
;*
;*
;**************************************************************************/



       Mov SI, StkPSG                           ; Restore PSG offset.

                                                ;           
       SaveReg<DS,SI,DI,BX>                     ; Save regs
       Mov  BX,DI                               ; Put KCB offset in BX
       Mov  DI,SI                               ; Put PSG offset in DI
       Mov  AX,1                                ; Indicate to NOT access the KCB
       Call PurgeKIB                            ; Now purge the KIB
       RestoreReg<BX,DI,SI,DS>                  ; Restore regs
                                                ;           


 .If < <Word Ptr ES:[DI].KCBPrev> eq 00h> AND   ; Previous = 0? AND
 .If < <Word Ptr ES:[DI].KCBPrev+2> eq 00h> AND ; Previous = 0?           
 .If < <Word Ptr ES:[DI].KCBNext> eq 00h> AND   ; Next = 0?
 .If < <Word Ptr ES:[DI].KCBNext+2> eq 00h>     ; Next = 0?           
    Mov Word Ptr [SI].KCBFirst, 00h             ; Yes, update the start/end
    Mov Word Ptr [SI].KCBFirst+2, 00h           ; Yes, update the start/end
    Mov Word Ptr [SI].KCBLast, 00h              ;  pointers in the PSG.
    Mov Word Ptr [SI].KCBLast+2, 00h            ;  pointers in the PSG.           
                                                
                                                ; Update the PSG "focus"
    Mov Word Ptr [SI].KCBAddress,00h            ; field to indicate that the default
    Mov Word Ptr [SI].KCBAddress+2,00h          ; KCB has the focus
                                                

 .Else NEAR                                     ; Else more than one KCB in
                                                ;  the chain.
    Mov AX, Word Ptr ES:[DI].KCBPrev+2          ; Get the physical
    Mov BX, Word Ptr ES:[DI].KCBPrev            ;  address of the
                                                ;  previous KCB.

    ;* AX:BX contain the physical address of the previous KCB.

    Mov CX, Word Ptr ES:[DI].KCBNext+2          ; Get the physical
    Mov DX, Word Ptr ES:[DI].KCBNext            ;  address of the
                                                ;  next KCB.

    ;* CX:DX contain the physical address of the next KCB.

    Push AX                                     ; Save the physical addresses
    Push BX                                     ;  of both the previous
    Push CX                                     ;  and the next
    Push DX                                     ;  KCB's.

   .If < AX eq 0 >                              ; Does previous pointer
                                                ;  indicate KCB was first?
      Mov Word Ptr [SI].KCBFirst, DX            ; Make the PSG point
      Mov Word Ptr [SI].KCBFirst+2, CX          ;  to the next KCB as
                                                ;  the first item.

      Mov AX, CX                               ; Put physical address in
      Mov BX, DX                               ;  proper register for DevHlp.
      Mov CX, KCBSIZE                          ; Specify size for DevHlp.
      Mov DH, ES_DI                            ; Specify receiving registers.
      Mov DL, DevHlp_PhysToVirt                ; Specify desired function.
      Call [DeviceHelp]                        ; ES:DI will contain virtual
                                               ;  address of the next KCB.
      Mov Word Ptr ES:[DI].KCBPrev, 00h        ; Mark this KCB as the
      Mov Word Ptr ES:[DI].KCBPrev+2, 00h      ; Mark this KCB as the,PTR 709460
                                               ;  first in the list.
      Pop DX                                   ; Restore the next KCB's
      Pop CX                                   ;  physical address.

   .Else                                       ; Else KCB wasn't first in the list.

      ;* (AX:BX already contains physical address of previous KCB)

      Mov CX, KCBSIZE                          ; Specify size for DevHlp.
      Mov DH, ES_DI                            ; Specify receiving registers.
      Mov DL, DevHlp_PhysToVirt                ; Specify desired function.
      Call [DeviceHelp]                        ; ES:DI will contain virtual
                                               ;  address of previous KCB.
      Pop DX                                   ; Restore the next KCB's
      Pop CX                                   ;  physical address.
      Mov Word Ptr ES:[DI].KCBNext+2, CX       ; Save it in the
      Mov Word Ptr ES:[DI].KCBNext, DX         ;  previous KCB as the
                                               ;  next item in list.

   .Endif                                      ; End KCB first in list check.

    Pop BX                                     ; Restore the physical addr.
    Pop AX                                     ; of the previous KCB.
    Mov SI, StkPSG                             ; Restore the PSG offset.

   .If < DX eq 0 >                             ; Does next pointer indicate
                                               ;  KCB was last in the list?
       Mov Word Ptr [SI].KCBLast, BX           ; Indicate that the
       Mov Word Ptr [SI].KCBLast+2, AX         ;  previous KCB is the
                                               ; last in the list.
       Mov CX, KCBSIZE                         ; Specify size for DevHlp.
       Mov DH, ES_DI                           ; Specify receiving registers.
       Mov DL, DevHlp_PhysToVirt               ; Specify desired function.
       Call [DeviceHelp]                       ; ES:DI will contain virtual
                                               ;  address of previous KCB.
       Mov Word Ptr ES:[DI].KCBNext, 00h       ; Mark the KCB as the
       Mov Word Ptr ES:[DI].KCBNext+2, 00h     ; Mark the KCB as the ,PTR 709460
                                               ; last in the list.

   .Else                                       ; Else KCB not last in list.

       Push AX                                 ; Save the previous KCB's
       Push BX                                 ;  physical address.
       Mov AX, CX                              ; Set DevHlp registers to
       Mov BX, DX                              ;  contain physical address
                                               ;  of next KCB.
       Mov CX, KCBSIZE                         ; Specify size for DevHlp.
       Mov DH, ES_DI                           ; Specify receiving registers.
       Mov DL, DevHlp_PhysToVirt               ; Specify desired function.
       Call [DeviceHelp]                       ; ES:DI will contain virtual
                                               ;  address of next KCB.

       Pop BX                                  ; Restore physical address
       Pop AX                                  ;  of previous KCB.
       Mov Word Ptr ES:[DI].KCBPrev+2, AX      ; Make this KCB point
       Mov Word Ptr ES:[DI].KCBPrev, BX        ;  to the proper item
                                               ; in the list.

   .Endif                                      ; End if KCB last in list check.

 .Endif                                        ; End if KCB was only one in
                                               ;  the list.

  ;* Finally, the links have been updated and the KCB to be freed
  ;* is no longer identified in the chain.  All that is left is to
  ;* check for a custom code page and free the memory allocated for
  ;* this KCB.

  Mov AX, Word Ptr KCBSave+2                   ; Get the physical address
  Mov BX, Word Ptr KCBSave                     ;  of the KCB to be killed.
  Mov CX, KCBSIZE                              ; Specify size for DevHlp.
  Mov DH, ES_DI                                ; Specify receiving registers.
  Mov DL, DevHlp_PhysToVirt                    ; Specify desired function.
  Call [DeviceHelp]                            ; ES:DI will contain virtual
                                               ;  address of doomed KCB.
  Call Unlock_CustomCP                         ; Free custom code page for KCB,
                                               ; if necessary. CF set if error.

;PTR B712571
.386p
  Push EAX                                     
  Mov  EAX, ES:[DI].KCBLinearAddr              ; Doomed KCB's linear address.
  Mov  DL, DevHlp_VMFree                       ; Specify desired DevHlp.
  Call [DeviceHelp]                            ; Remove from memory.
  Pop  EAX                                     
.286p
;End B712571
                                               
  UnPhysToVirt                                 ; Macro to undo PhysToVirts.

  Ret
EndProc FreeKCB

BREAK <Unlock_CustomCP subroutine>
Public Unlock_CustomCP
Procedure Unlock_CustomCP, HYBRID

;/***************************************************************************
;*
;* FUNCTION NAME = Unlock_CustomCP
;*
;* DESCRIPTION   = Check if KCB is using a custom translate table
;*
;*                 Check if KCB is using a custom translate table. If
;*                 it is a custom table, turn off custom code page bit.
;*
;* INPUT         = ES:DI = KCB to check for a custom code page
;*
;* OUTPUT        = NONE
;*
;*
;* RETURN-NORMAL = AX, BX, DL altered
;*
;* RETURN-ERROR  = Carry set if unlock error
;*
;*
;**************************************************************************/

    ;*           Made change to use VMFree of the linear address


 .386p
  Test   ES:[DI].KCBFlags, CUSTOMCP       ; Check if KCB using a custom table
 .If < nz >                               ; If it is a custom table,
    And  ES:[DI].KCBFlags, Not CUSTOMCP   ; Turn off custom code page bit.
    Mov  EAX, ES:[DI].KCBCustCPLH         ; The linear address of mem to give up
    Mov  DL, DevHlp_VMFree                ; Specify desired DevHlp.
    Call [DeviceHelp]                     ; Remove from memory.
 .Endif                                   ; End if not a custom table.
 .286p

  Ret
EndProc Unlock_CustomCP


;;***************   THIS FUNCTION IS NEVER CALLED *******************
;;
;;BREAK <CheckTOEventID subroutine>
;;Public CheckTOEventID
;;CheckTOEventID          Proc
;;
;/**************************************************************************
;*   FUNCTION NAME = CheckTOEventID
;*
;*   DESCRIPTION   = Run a thread blocked waiting on an interrupt,
;*                   if necessary.
;*
;*                   This routine checks a given Timeout Event ID for a
;*                   non-zero value (indicating a thread is waiting on
;*                   an interrupt).  If it is non-zero, the routine
;*                   wakes the thread up.
;*
;*    INPUT: None
;*
;*    EXIT-NORMAL: Return to caller of routine.
;*
;*    EXIT-ERROR:  None.  All exits are through NORMAL above.
;*
;*    EFFECTS:
;*          Blocked thread may be run
;*
;*
;**************************************************************************/
;;
;;  SaveReg<AX,BX,DX,DI>                    ; macro to save regs
;;  Mov DI,TimerPSG                         ; Get the PSG pointer for calling SG
;; .If <<Word Ptr [DI].TOEventID> ne 0>     ; Is a thread waiting to be run
;;    Mov BX,Word Ptr [DI].TOEventID        ; Get Lo word of Event ID from block
;;    Mov AX,Word Ptr [DI].TOEventID+2      ; Get Hi word of Event ID from block
;;    Mov DL,DevHlp_ProcRun                 ; Specify DevHlp function
;;    Call [DeviceHelp]                     ; Run the blocked thread
;; .Endif                                   ;
;;  RestoreReg<DI,DX,BX,AX>                 ; macro to restore regs
;;  Ret
;;
;;CheckTOEventID  Endp
;;

BREAK <BlockCheck subroutine>
Public BlockCheck
Procedure BlockCheck, HYBRID

;*************************************************************************
;*
;* FUNCTION NAME = BlockCheck
;*
;* DESCRIPTION   = Serialize access to the KIB, block if threads
;*                 ahead.
;*
;*                 Attempts to obtain the KIB semaphore via DevHlp_
;*                 SemRequest.  If the semaphore isalready set, then
;*                 another IOCTL thread is using the KIB and the
;*                 requesting thread is blocked in the DevHlp until the
;*                 semaphore is cleared (reference UnBlockCheck).
;*
;* NOTES         = Sequential access to the KIB is NOT guaranteed with
;*                 this method, but since the IOCTL requests themselves
;*                 cannot be guaranteed to be sequential coming into the
;*                 device driver, it is a moot point.
;*
;*                 There is one semaphore for each KIB resource, hence
;*                 one semaphore for each KCB.
;*
;* INPUT         = ES:BX = Req Block Address
;*
;* RETURN-NORMAL = Return to caller of routine.
;*
;* RETURN-ERROR  = None. All exits are through NORMAL above.
;*
;* EFFECTS:
;*              DX is altered.
;*
;*              Error bit set in request packet for any error
;*              return from DevHlp_SemRequest.  Char call interrupted
;*              status set if appropriate, otherwise a general
;*              failure is indicated.
;*************************************************************************

      Push   AX                                ; Save caller's registers.
      Push   CX                                ; 
      Push   DI                                ; 

      Mov    DH, ES_DI                         ; Set AccessKCB destination regs.
      Call   AccessKCB                         ; Get address of current KCB.
      Add    DI, KCBBufferSem                  ; Calculate KIB RAM semaphore addr.
      Mov    BX, DI                            ; AX:BX = virtual address of KIB
      Mov    AX, ES                            ;  RAM semaphore.
      Mov    CX, INFINITETIME                  ; Set timeout value
      Mov    DI, CX                            ;  to wait forever.
      Mov    DL, DevHlp_SemRequest             ; Set DevHlp function.

      Call   DeviceHelp                        ; Carry and AX set on return if
                                               ;  error...
      Pop    DI                                ; Restore caller's registers.
      Pop    CX                                ; 
      LES    BX, Dword Ptr StkRPO              ; Restore request packet address.

     .If < c >                                 ; If error did occur in DevHlp,
       .If < AX e ERROR_INTERRUPT >            ; If due to interruption,
          Mov  ES:[BX+PktStatus],CCIError      ; Set Char Call Interrupted error status
          Mov  DL,DevHlp_DevDone               ; Set Device Help function.
          Call DeviceHelp
       .Else                                   ; Not interrupted error,
          Mov ES:[BX].PktStatus,GenError       ; Indicate general failure status.
       .Endif                                  ; End interruption check.
     .Endif                                    ; End if DevHlp error.

      Pop AX                                   ; Restore caller's register.

      Ret

EndProc BlockCheck

BREAK <UnBlockCheck subroutine>
Public UnBlockCheck
Procedure UnBlockCheck, HYBRID

;/***************************************************************************
;*
;* FUNCTION NAME =  UnBlockCheck
;*
;* DESCRIPTION   =  Clear serialization semaphore.
;*
;*                  Clears the KIB RAM semaphore, allowing another
;*                  thread to claim the semaphore and access the KIB.
;*
;* NOTES         =  The order in which serialized requests are awakened
;*                  depends upon the thread's priority.  Sequencing of
;*                  IOCTL requests is not guaranteed (reference the
;*                  BlockCheck routine).
;*
;* INPUT         = ES:BX = Request Packet Address
;*
;* OUTPUT        = NONE
;*
;*
;* RETURN-NORMAL = Return to caller of routine.
;*
;* RETURN-ERROR  = None. All exits are through NORMAL above.
;*
;*
;**************************************************************************/


      Push    AX                                ; Save caller's registers.
      Push    DI                                ; 

      Mov     DH, ES_DI                         ; Set AccessKCB destination regs.
      Call    AccessKCB                         ; Get address of current KCB.
      Add     DI, KCBBufferSem                  ; Calculate KIB RAM semaphore addr.

      Mov     BX, DI                            ; AX:BX = virtual address of KIB
      Mov     AX, ES                            ;  RAM semaphore.
      Mov     DL, DevHlp_SemClear               ; Set DevHlp function.
      Call    DeviceHelp                        ; Clear serialization semaphore.

      Pop     DI                                ; Restore caller's registers.
      Pop     AX                                ; 
      LES     BX, Dword Ptr StkRPO              ; Restore request packet address.

      Ret

EndProc UnBlockCheck

Break <PutInVDMQ>
Public PutInVDMQ
PutInVDMQ Proc

;/***************************************************************************
;*
;* FUNCTION NAME = PutInVDMQ
;*
;* DESCRIPTION   = Put scan code in VDM session Queue
;*
;*
;*                 This routine places a new element into a linked list
;*                 (queue) for a VDM session.  We will only place elements
;*                 into this queue for VDM sessions that we have not yet
;*                 received a VDMCreate call for.  These queue elements
;*                 comprise the list of typahead scan codes for a particular
;*                 VDM.  The queue will be emptied when the PDD receives a
;*                 VDMCreated call.  The elements in the queue will then be
;*                 sent to the VDD through the EventNotify function.
;*
;* NOTES         = 1) The queue can only hold a MAX_VDM_SC number of scan codes
;*
;*                 2) Queue will be emptied on next VDMCreated call
;*
;*                 3) Queue is assumed to only have scan codes from 1 VDM
;*                    session in it at any one time.
;*
;* INPUT         = Scan code is in AX
;*                 Keyboard data segment in DS
;*
;* OUTPUT        = NONE
;*                 Scan codes for a particular VDM are buffered up to pre-
;*                 serve typahead functionallity when creating new VDMs.
;*
;* RETURN-NORMAL = Scan code is queued as next element in list.
;*                 Return to interrupt handler.
;*
;*
;* RETURN-ERROR  = Queue is full, speaker is beeped.  Scan code is NOT
;*                 saved.  Return to interrupt handler.
;*
;**************************************************************************/



  .If <VDMQInPtr ne 0>                 ; If Q is not full
    SaveReg<BX>
    Xor BH,BH                          ; Clear out high portion
    Mov BL,VDMQInPtr                   ; Get next slot in Q for a SC insert
    Add BX, Offset VDMKeyQ             ; Add actual Q address
    Mov [BX],AL                        ; Move scan code into Q
    Dec VDMQInPtr                      ; Adjust ptr to next slot
    RestoreReg<BX>
  .Else                                ; Q is full
    KbdBeep 3,1000,13                  ; Use macro to beep speaker
  .Endif
  Ret

PutInVDMQ endp

BREAK <HotKeyCheck subroutine>
Public HotKeyCheck

;/***************************************************************************
;*
;* FUNCTION NAME = HotKeyCheck
;*
;* DESCRIPTION   = Check for Session Manager Hot Key with correct
;*                 Shift State.
;*
;*
;*                 Called by the Interrupt Handler on every
;*                 keystroke, and by the Monitor Dispatcher notification
;*                 routine on every keystroke inserted by a monitor.
;*
;*                 Checks for lone press/release of the defined Session
;*                 Manager hot key, with only the required shift state
;*                 accompanying it. Sends the event "event_SMKey" when
;*                 it sees it.
;*
;* INPUT         =
;*                 SI = Ptr to a KeyPacket  DI = Ptr to Current PSG
;*                 DH = HotKeyDown bit if called before monitor chain.
;*                      MHotKeyDown bit if called after monitor chain.
;*
;*
;* OUTPUT        = NONE Alters: AX, BX, DX
;*                      The SMKey Event could be sent.
;*
;* RETURN-NORMAL = Return to caller of routine.
;*
;* RETURN-ERROR  = None.  All exits are through NORMAL above.
;*
;*
;**************************************************************************/


HotKeyCheck   Proc
  Test LockupFlags,Lockup                   ;            If Desktop's Lockup
 .If <nz>                                   ;            is on, do not reboot
     Ret                                    ;            the system.
 .Endif                                     ;           

                                            ;           
  Push AX                                   ; Save AX
  Mov AH,Byte Ptr [SI].KPacketLen+1         ; Now scan code is in AH.
 .If <bit [SI].DDFlags z SecondaryKey> AND  ; If an E0 scan DID NOT precede Scan
 .If < AH eq 47h >                          ; If scan is 47h
    Pop AX                                  ; Number Pad HOME key was pressed
    Ret                                     ; Get out of here
 .Endif                                     ; Endif
  Pop AX                                    ; Restore AX

  Push CX
  And AX, Not HKResMask                     
                                            ; bits in shift state for
                                            ; comparison with hot key table.
  Mov BX,Offset HotKeyTable                 ; Get Hot Key table address

  Mov CX,1                                  ; Set Hot Key table entry index

 .While <CL le HotKeyCount> NEAR            ; Loop through hot key table
    Test [SI].DDFlags,KeyBreak              ; Test if this is key break
   .If <nz> NEAR                            ; If it is
       Push AX                              ; Save the masked shift state
       Mov AH,Byte Ptr [SI].KPacketLen+1    ; Now scan code is in AH.
      .If <AH e [BX].HotKeyBreak> Near      ; If this is the hot key break scan
           Test [OtherFlags],DH             ; Check if stayed down correctly.
          .If <nz>  NEAR                    ; Has it?
             Push BX                        ; Save Hot Key table pointer

.386p                                           
             Push EAX                           
             And [SI].DDFlags, NOT KeyTypeMask  
            .If <VDMFocus eq 1> AND             ; If a VDM has the FOCUS  AND
            .If <HKBypass ne 0>                 ; If a Hot Key is bypassed
               Push CX
               Push SI                          ; Save offset of Keypacket
               Mov si, offset BypassTbl         ; Get offset of BypassTbl
               xor cx, cx
               Mov bl, [bx].HotKeyBreak         ; Get the Hot Key break scan
               Mov eax, 1                       ; Set up a dynamic bit mask.
              .While <NONZERO eax>              ; While looping in BypassTbl
                .If <bit HKBypass nz eax> AND   ; If HKBypass bit matches EAX.
                .If <bl eq [si].HKB> AND        ; If a hot key match.
                 mov cx, Word Ptr [si].Shft     ; Get Table Shift value.
                .If <bit ShiftData nz cx>       ; If a shift match.
                   Pop SI                       ; Get offset of Keypacket.
                   Pop CX
                   Jmp NoSendEvent              ; Don't issue sendevent.
                .Endif                          ; 
                 Add si, HKTableSiz             ; Increment by structure size.
                 Shl eax, 1                     ; Shift the bit and try again.
              .Endwhile                         ; 
               Pop SI                           ; Get offset of Keypacket
               Pop CX                           ; 
            .Endif                              ; 

            .If <VDMFocus eq 0> AND             ; If VDM doesn't have FOCUS  AND
            .If <[bx].HotKeyBreak eq 0C7h>      ; If break of HOME HOT KEY
                                                ; ONLY VDM'S ACT ON THIS HOTKEY
                Jmp NoSendEvent                 ; Don't issue sendevent.
            .Endif

             Mov BX,FoundHKID                   ; Get the hot key ID
             Mov AH,event_SM_Kbd                ; Get event name to signal.
             Mov DL,DevHlp_SendEvent            ; Indicate Signal.
             Push DX                            ; Save DX cause DevHlp zaps it.
             Call [DeviceHelp]                  ; Go do it.
             Pop DX                             ; Restore DX.
             Or [SI].DDFlags, HotKeyPacket      ; PTM 3940: as the hot key.

NoSendEvent:

             Mov DL,DH                          ; Put hotkey flag in DL
              .If <bit HKBypass nz 01>          ; If Alt+Esc bypass bit set
                .If <bit ShiftData z 0a00h>     ; If NOT left or right ALT
                   Or IntFlags, Not_VScan       ; Don't send scan to VKBD
                .Endif                          ; 
              .Elseif <bit HKBypass nz 02>      ; If Ctrl+Esc bypass bit set
                .If <bit ShiftData z 0500h>     ; If NOT left or right CTRL
                   Or IntFlags, Not_VScan       ; Don't send scan to VKBD
                .Endif                          ; 
              .Elseif <bit HKBypass nz 04>      ; If Alt+Home bypass bit set
                .If <bit ShiftData z 0a00h>     ; If NOT left or right ALT
                   Or IntFlags, Not_VScan       ; Don't send scan to VKBD
                .Endif                          ; 
              .Else                             ; 
                 Or IntFlags, Not_VScan         ; Don't send scan to VKBD
              .Endif                            ; 

             Mov DH,DL                          ; Put Hot key flag back
             Not DH                             ; So set mask.
             And [OtherFlags],DH                ; Clear Hot Key Latch.
             Pop EAX                            
             Pop BX                             ; Restore hot key table ptr
             Mov CL,HotKeyCount                 ; Set end of loop condition
             Or OtherFlags,HotKeyFound          ; Set flag indicating found it

          .Else                                 ; Else Hot Key hasn't stayed
             Test OtherFlags,HotKeyBrkPend      ; down correctly
            .If <nz>                            
               And [SI].DDFlags, NOT KeyTypeMask; of hotkey is also UnDefKey
               Or  [SI].DDFlags, UnDefKey       ; If HotKey did not stay down
                                                ; correctly.
                                                
              .If <bit HKBypass nz 01>          ; If Alt+Esc bypass bit set
                .If <bit ShiftData z 0a00h>     ; If NOT left or right ALT
                   Or IntFlags, Not_VScan       ; Don't send scan to VKBD
                .Endif                          ; 
              .Elseif <bit HKBypass nz 02>      ; If Ctrl+Esc bypass bit set
                .If <bit ShiftData z 0500h>     ; If NOT left or right CTRL
                   Or IntFlags, Not_VScan       ; Don't send scan to VKBD
                .Endif                          ; 
              .Elseif <bit HKBypass nz 04>      ; If Alt+Home bypass bit set
                .If <bit ShiftData z 0a00h>     ; If NOT left or right ALT
                   Or IntFlags, Not_VScan       ; Don't send scan to VKBD
                .Endif                          ; 
              .Else                             ; 
                 Or IntFlags, Not_VScan         ; Don't send scan to VKBD
              .Endif                            ; 

            .Endif

          .Endif                                ; Endif Hot Key hasn't stayed down
                                                ; correctly

           And OtherFlags, NOT HotKeyBrkPend    
           Pop AX                               ; Restore masked shift state
      .Else  NEAR                               ; Else not this hot key break
           Push EAX                             
           Test [OtherFlags],DH                 ; If stayed down correctly.
          .If <nz> NEAR                         ; If it did
                                                ; Check if a shift key break
             Mov AL,Byte Ptr [SI].DDFlags       ; Get low byte of DD Flags
             And AL,KeyTypeMask                 ; Mask off key type.
            .If <AL eq ShiftMask> NEAR
                                                
              .If <HKTemp e 047h>               ; If last key was a 47h HOT KEY
                 Mov BX,Offset HotKeyTable      ; Get Hot Key table address
                 Add BX,HKEntrySize * 2         ; Point index to C7h value
              .Endif
                                                ; Gets here due to BREAK of the
                                                ; alt or ctrl but ASSUMES the
                                                ; hotkey break key was 81.  We
                                                ; now check to make sure about
                                                ; this, it could be 9c!

                                                
             And [SI].DDFlags, NOT KeyTypeMask  
            .If <VDMFocus eq 1> AND             ; If a VDM has the FOCUS  AND
            .If <HKBypass ne 0>                 ; If a Hot Key is bypassed
               Push CX
               Push SI                          ; Save offset of Keypacket
               Mov si, offset BypassTbl         ; Get offset of BypassTbl
               xor cx, cx
               Mov bl, [bx].HotKeyBreak         ; Get the Hot Key break scan
               Mov eax, 1                       ; Set up a dynamic bit mask.
              .While <NONZERO eax>              ; While looping in BypassTbl
                .If <bit HKBypass nz eax> AND   ; If HKBypass bit matches EAX.
                .If <bl eq [si].HKB> AND        ; If a hot key match.
                 mov cx, Word Ptr [si].Shft     ; Get Table Shift value.
                .If <bit ShiftData nz cx>       ; If a shift match.
                   Pop SI                       ; Get offset of Keypacket.
                   Pop CX
                   Jmp NoSendEvent2             ; Don't issue sendevent.
                .Endif                          ; 
                 Add si, HKTableSiz             ; Increment by structure size.
                 Shl eax, 1                     ; Shift the bit and try again.
              .Endwhile                         ; 
               Pop SI                           ; Get offset of Keypacket
               Pop CX
            .Endif                              ; 

            .If <VDMFocus eq 0> AND             ; If VDM doesn't have FOCUS  AND
            .If <[bx].HotKeyBreak eq 0C7h>      ; If break of Home HOT KEY
                                                ; ONLY VDM'S ACT ON THIS HOTKEY
                Jmp NoSendEvent2                ; Don't issue sendevent.
            .Endif

             Mov BX,FoundHKID                   ; Get the hot key ID
             Mov AH,event_SM_Kbd                ; Get event name to signal.
             Mov DL,DevHlp_SendEvent            ; Indicate Signal.
             Push DX                            ; Save DX 'cause DevHlp zaps it.
             Call [DeviceHelp]                  ; Go do it.
             Pop DX                             ; Restore DX.
             Or [SI].DDFlags, HotKeyPacket      

NoSendEvent2:                                   

                 Mov DL,DH                      ; Put hotkey flag in DL.
                                                
                                                ; It prevented sending shift breaks
;;* PTR B720337  Or IntFlags, Not_VScan         ; Turn on flag saying this
                                                ; does not go to the VKBD.
                 Mov DH,DL                      ; Put Hot key flag back where it goes
                 Not DH                         ; So set mask.
                 And [OtherFlags],DH            ; Clear Hot Key Latch.
                 Or OtherFlags,HotKeyFound      ; Set flag indicating we found it
                 Mov CL,HotKeyCount             ; Set end of loop condition
            .Endif                              
                                                
          .Endif                                ; Endif the hot key was down
           Pop EAX                              
           Pop AX                               ; Restore masked current shift state
      .Endif                                    ; Endif Hot Key Break or not
   .Else  NEAR                                  ; Else its a make scan code
       Push CX                                  ; Save the loop counter
       Push DX                                  ; Save the hot key down mask
       Mov CH,Byte Ptr [SI].KPacketLen+1        ; Now scan code is in AH.
      .If <CH e [BX].HotKeyMake> AND NEAR       ; If this is the hot key make scan
       Mov DX,[BX].HotKeyShift                  ; Get the required shift state
      .if <dx eq ax> or                         ; could be the null shift st
       Test AX,DX                               ; Check if requred shift keys are down
      .If <nz> AND  NEAR                        ; If they are AND...
       Not DX                                   ; Make list of keys not allowed
       Test AX,DX                               ; Check if any disallowed keys are
                                                ; down
      .If <z> NEAR                              ; If not
                                                
         Mov ShiftData, AX                      ; OK, so save the Shift key value in
                                                ; case it's SendEvent is to be bypassed
         Mov HKTemp, CH                         ; Store value for later check
         Pop DX                                 ; Restore the hot key down flag
         Pop CX                                 ; Restore CX
         Mov CL,HotKeyCount                     ; Set end of loop condition
         Mov BX,[BX].HotKeyID                   ; Get the hot key ID
         Mov FoundHKID,BX                       ; Save it
         Or OtherFlags,DH                       ; Turn on the hot key down bit
         Or OtherFlags,HotKeyFound              ; Set flag indicating we found it

                                                
                                                ; The HOME key is an extended key so
                                                ; in PM it is handled differently than
                                                ; the ESC key.  For this reason, in
                                                ; PM, We must not set the 3Fh flag.
                                                ; This is so that an PM APP can receive
                                                ; the key if the hot key is bypassed.
                                                ; Ok.....here is the code below
       .If <bit [DI].PSGFlags nz SQMODE> AND    ; If Single Queue mode.
       .If < HKTemp eq 047h >                   ; If HOME key
         nop                                    ; For reason stated above.
       .Else                                    ; Else
         Or [SI].DDFlags,3Fh                    ; Mark this key packet "undefined"
       .Endif                                   ; Endif


        ;DCR1195
        .If <bit HKBypass nz 01>                ; If Alt+Esc bypass bit set
          .If <bit ShiftData z 0a00h>           ; If NOT left or right ALT
             Or IntFlags, Not_VScan             ; Don't send scan to VKBD
          .Endif                                ; 
        .Elseif <bit HKBypass nz 02>            ; If ctrl+Esc bypass bit set
          .If <bit ShiftData z 0500h>           ; If NOT left or right CTRL
             Or IntFlags, Not_VScan             ; Don't send scan to VKBD
          .Endif                                ; 
        .Elseif <bit HKBypass nz 04>            ; If Alt+Home bypass bit set
          .If <bit ShiftData z 0a00h>           ; If NOT left or right ALT
             Or IntFlags, Not_VScan             ; Don't send scan to VKBD
          .Endif                                ; 
        .Else                                   ; 
           Or IntFlags, Not_VScan               ; Don't send scan to VKBD
        .Endif                                  ; 

.286p
         Or OtherFlags,HotKeyBrkPend            
                                                ; Indicate a HotKey break is pending.
      .Else                                     ; Not VALID hot key make
         Pop DX                                 ; Restore the hot key down flag
         Pop CX                                 ; Restore stack
      .Endif                                    ; Endif VALID hot key make or not
   .Endif                                       ; Endif Break scan code or not
    Inc CX                                      ; Increment hot key table entry index
    Add BX,HKEntrySize                          ; Multipy it index by size of entry
 .EndWhile


  Test OtherFlags,HotKeyFound                   ; Check if hot key was found in table
 .If <z>                                        ; If not, this is not the hotkey.
    Mov AL,Byte Ptr [SI].DDFlags                ; Get low byte of DD Flags from packet.
    And AL,KeyTypeMask                          ; Mask off key type.
   .If <z> OR                                   ; If not a regular key, keep checking...
   .If <AL a 4>                                 ; If not a special keyboard code...
      Not DH                                    ;      ...then set a mask to...
      And [OtherFlags],DH                       ;          ...clear Hot Key Latch.
   .Endif
 .Else                                          ; Else it was found
    And OtherFlags,Not HotKeyFound              ; clear the flag for next time
 .Endif
  Pop CX
  Push ES                                       
  Push DI                                       ; 
  Mov DH, ES_DI                                 ; 
  Call AccessKCB                                ; 
  Mov BX,ES:[DI].KCBIxOffCP                     ; 
  Pop DI                                        ; 
 .If <bit MiscFlags nz MLayerMode> AND          ; If Multi Layer codepage is supported
 .If <BX ne 2>                                  ; If we are in layered codepage index
    Call Layer_HKChk                            
 .Endif                                         ; 
  Pop  ES                                       ; 
  Ret

;@@ PTM 5449 - END

HotKeyCheck Endp

BREAK <VDMPreXlate subroutine>
Public VDMPreXlate
VDMPreXlate  Proc

;/***************************************************************************
;*
;* FUNCTION NAME = VDMPreXlate
;*
;* DESCRIPTION   = Pre keystroke translation for a VDM scan code
;*
;*                 Called by VDMTranslate to check for various special
;*                 scan codes that must be handled before we send these
;*                 scan codes to KBDXlate for translation.
;*
;* INPUT         = AL         = Raw Scan Code
;*                 DS:SI      = Ptr to KeyPacket1
;*                 DS:StkTmpL = Ptr to KeyPacket2
;*                 DS:DI      = Ptr to Translation Flags Packet
;*                 ES:BX      = Ptr to Translate Table
;*
;*
;* OUTPUT        = NONE
;*
;* RETURN-NORMAL =  Return to caller of routine.
;*
;* RETURN-ERROR  =  None.  All exits are through NORMAL above.
;*
;*
;**************************************************************************/



  .If <AL eq OtherKey>               ; If scan code is E0
    Or [DI].XKeyFlags,SecPrefix      ; Extended key just seen
    Or [SI].DDFlags,SecPrefixCode    ; zero out DDFlags in KeyPacket1

  .Elseif <AL eq OtherKey2>          ; If scan code is E1
    Or [DI].XKeyFlags,SecPrefix      ; Extended key just seen
    Or [DI].XlateFlags,E1Prefix      ; E1 prefix just seen
    Or [SI].DDFlags,SecPrefixCode    ; zero out DDFlags in KeyPacket1

  .Elseif <AL eq ACK>                ; If scan code is FA
    Or [SI].DDFlags,AckCode          ; Update DDFlags for this ACK

  .Elseif <AL eq Resend>             ; If scan code is FE
    Or [SI].DDFlags,ResendCode       ; Update DDFlags for this Resend

  .Elseif <AL eq BufferFull>         ; If scan code is FF
    Or [SI].DDFlags,OverRunCode      ; Update DDFlags for this OverRun

  .Else
    And [DI].XSpecFlags,0            ; Clear out the XSpecFlags.
    Call KbdXlate                    ; XLate the scan code in KeyPacket1
    Call VDMScanFilter               ; Filter out special scans
  .Endif

  Ret

VDMPreXlate  Endp


BREAK <SendVDDSC subroutine>
Public SendVDDSC
SendVDDSC Proc

;/***************************************************************************
;*
;* FUNCTION NAME = SendVDDSC
;*
;* DESCRIPTION   = Send the scan code to the VDD
;*
;*                 This routine calls the VKBD with the PDDToVDDSCPkt
;*                 filled. This packet contains the scan code and screen
;*                 group number that it belongs in.
;*
;*
;* INPUT         = PDDtoVDDSCPkt
;*
;*
;* OUTPUT        = NONE
;*
;*
;* RETURN-NORMAL = Return to caller of routine.
;*
;* RETURN-ERROR  = None.  All exits are through NORMAL above.
;*
;*
;**************************************************************************/


 .If <AL ne BufferFull>                   ;            If scan code is NOT an FFh AND
                                          ; Don't send FFh HW responses to
                                          ; the VKBD.
  pusha                                   ; Push all regs (VDDEntryPoint)
                                          ; is trashing them
  SaveReg<ES>                             ; VDD is also trashing ES
  Push    Word Ptr 0                      ; Send zero in high word
  Push    Word Ptr VKBDCMD_INTEVENT       ; VKBDCMD_INTEVENT
  Push    DS                              ; Send our Data Selector
  Push    Offset PDDtoVDDSCPkt            ; Send offset to packet from our DS
  Push    Word Ptr 0                      ; Send zero in high word
  Push    Word Ptr 0                      ; Send zero in high word
  .386p
  Call Fword Ptr VDDEntryPoint            ; Call the VDD entry point
  .286p
  RestoreReg<ES>                          ; Get VDDs data sel back
  popa                                    ; Push all regs (VDDEntryPoint)
                                          ; is trashing them
 .Endif
  Ret

SendVDDSC Endp


BREAK <Update_SG_SS subroutine>
Public Update_SG_SS
Procedure Update_SG_SS, HYBRID

;/***************************************************************************
;*
;* FUNCTION NAME = Update_SG_SS
;*
;* DESCRIPTION   = Update the shift state for the screen group
;*
;*                 This routine will synch up the logical and physical
;*                 shift states for both the incoming and outgoing
;*                 screen group. It will set a bit in the Sendit field
;*                 in the ShiftKeyTable which indicates which shift ke
;*                 scan code must be send to both in outgoing and
;*                 incoming screen groups.
;*
;*
;* INPUT         = CurSG = Outgoing Screen group
;*                 AX    = Incoming screen group
;*                 ES:DI = Outgoing session's KCB
;*
;*
;*
;* OUTPUT        = NONE
;*
;*
;* RETURN-NORMAL =
;*                  Return to caller of routine.
;* RETURN-ERROR  =
;*                  None.  All exits are through NORMAL above.
;*
;**************************************************************************/


  Push    ES                                ; 86188 - Save ES
  Pusha

   Mov     DX,ES:[DI].KCBShift             ; Current screen group's Shiftstate
  .If <bit DX nz SST_Mask>                  ; If any SST entries are down

      ;*
      ;* Loop thru all the SST entries to see if any shift keys are currently
      ;* down. If they are mark the field which indicates that the scan code
      ;* for that shift key must be sent.
      ;*
     Lea    BX, ShiftScanTable              ; Address of the table
     Xor    CL,CL                           ; Start at entry zero
     .Repeat

        .If <bit DX nz [BX].SST_Shift>      ; If this shift key is down
           Inc byte ptr [BX].SST_Sendit     ; We must send a scan for it
        .Endif
        Add    BX,SST_Size                  ; Point to next entry
        Inc    CL                           ; Increment entries seen

     .Until <CL eq SST_Entries>             ; Loop thru all entries

     ;*
     ;* At this point all shift key scan codes that must be sent are marked in
     ;* the ShiftScanTable. We will now send break packets for these scans to
     ;* the outgoing screen group.
     ;*

     Mov    CH,80h                          ; These scans will be breaks
     Or     AIMFlags, NonAIMScan            ; AIM Do not update the StickyTbl
     Call   Send_SScans                     ; Send the scans to the SG
     And    AIMFlags, NOT NonAIMScan        ; AIM Begin to update the StickyTbl again.

        ;*
        ;* Since we have now sent break packets for all the shift keys currently down
        ;* to the outgoing SG, we must update the Shift Flags field for this outgoing
        ;* SG in both the KCB and PSG.
        ;*

     And    DX,NOT SST_Mask                 ; Reset Shift State
     Mov    ES:[DI].KCBShift,DX             ; Put new shift state in the KCB

     Mov SI,CurSG                           ; Outgoing screen group number
     .If <SI ge sgidFirstVDM>               ; If VDM
        Mov SI,Dos3xBoxSG                   ; Use SG number 2
     .Endif

     Shl    SI,1                            ; Make SG # into an offsett
     Mov    SI,[PSGPointers+SI]             ; Get pointer to outgoing SGs PSG
     Mov    [SI].ShiftFlags,DX              ; Reset Shift State in PSG

; At this point all shift key scan codes that must be sent are marked in
; the ShiftScanTable. We will now send make packets for these scans to
; the incoming screen group.

     Mov CurSG,AX                           ; Make incoming SG active SG
     .If <CurSG ge sgidFirstVDM>            ; If VDM SG
        Mov DI,Dos3xBoxSG                   ; Use SG number 2
     .Else
        Mov DI,CurSG                        ; Get PSG for incoming SG
     .Endif
     Shl   DI,1
     Mov   DI,[PSGPointers+DI]
     Mov   StkPSG,DI                        ; Update Local Stack
     Mov   DH,ES_DI                         ; Next we must get the KCB for this
     Call  AccessKCB                        ; PSG and update the Local Stack.
     Mov   StkKCBO, DI                      ; The Local Stack variable are used
     Mov   StkKCBS, ES                      ; by the scan code processor

     Xor   CH,CH                            ; These scans will be makes
     Or    AIMFlags, NonAIMScan             ; AIM Do not update the StickyTbl
     Call  Send_SScans                      ; Send the scans to the SG
     And   AIMFlags, NOT NonAIMScan         ; AIM Begin to update the StickyTbl again.

  .Endif

  Popa
  Pop     ES                                ; 86188 - Restore ES
  Ret

EndProc Update_SG_SS

BREAK <Send_SScans subroutine>


;/***************************************************************************
;*
;* FUNCTION NAME = Send_SScans
;*
;* DESCRIPTION   = Send the shift key scans
;*
;*                 This routines sends break packets for all shift keys
;*                 that are currently down to the outgoing screen group
;*                 and will send make packets for all the shift keys
;*                 that are currently down to the new screen group.
;*
;* INPUT         = CH = 00h - Send Shift Key Makes
;*                 CH = 80h - Send Shift Key Breaks
;*
;* OUTPUT        = NONE
;*
;*
;* RETURN-NORMAL = Return to caller of routine.
;*
;* RETURN-ERROR  = None.  All exits are through NORMAL above
;*
;*
;**************************************************************************/


Public Send_SScans
Send_SScans Proc

  Pusha
  Lea    BX, ShiftScanTable                  ; Address of the table
  Xor    CL,CL                               ; Start at entry zero
  .Repeat
     .If <[BX].SST_Sendit ne 0>              ; If this shift key should be sent
        Mov    AL,[BX].SST_XKFlags           ; Get XKeyFlags value
        Or     IntFlagArea.XKeyFlags,AL      ; Turn on bit for E0 keys
        Mov    AL,byte ptr [BX].SST_Scan     ; Get shift key scan code
        Add    AL,CH                         ; Add break bit if needed

                                             
        Push ax                              ; Save original scan code
       .If <[bx].SST_XKFlags eq SecPrefix>   ; If scan needs a SecPrefix
          Pusha
          Push    ES
          mov     ax, 00e0h                  ; Make a fake E0h scan code
          Call    ProcessScanCode            ; Process this fake scan code
          Pop     ES                         ; 
          Popa
       .Endif
        Pop     ax                           ; Restore original scan code

        Pusha                                ; Save
        Push    ES                           
        Call    ProcessScanCode              ; Process thi
        Pop     ES                           ; Restore ES
        Popa                                 
        Mov     [BX].SST_Sendit,CH           ; NONZERO = make must still be sent
     .Endif

     Add BX,SST_Size                         ; 
     Inc CL                                  ; Increment entries seen

  .Until <CL eq SST_Entries>                 ; Loop thru all entries

  Popa
  Ret

Send_SScans Endp

BREAK <VDMScanFilter>

;/***************************************************************************
;*
;* FUNCTION NAME = VDMScanFilter
;*
;* DESCRIPTION   = Filter out scans that have certain meaning
;*
;*                 This routines filters out special scan codes that
;*                 cause abnormal output in a VDM screen group.
;*
;*                  1. Hot Key Scans accounted for a screen group switch
;*                     at interrupt time. We still sent these scans to
;*                     the VKBD at interrupt time. When these scans come
;*                     in a VDM time to be translated (VDMXLate) they
;*                     should not be flagged as HotKey scans. If they
;*                     are not flagged some wierd side effects can happen.
;*                     Ex. They will be used as wakeup keys.
;*
;* INPUT         = DS = VKBDs data sel for KeyPacket1
;*                 SI = VKBDs offset to KeyPacket1
;*                 DI = VKBDs offset to XLate Flags
;*
;*
;*
;* OUTPUT        = NONE
;*
;*
;* RETURN-NORMAL =  Return to caller of routine.
;*
;* RETURN-ERROR  =  None.  All exits are through NORMAL above.
;*
;*
;**************************************************************************/


Public VDMScanFilter
VDMScanFilter Proc

  Mov AL,Byte Ptr [SI].KPacketLen+1        ; Get scan code

                                           
 .If <AL eq 52h> OR                        ; If Insert Key make or break
 .If <AL eq 52h+BreakBit>
                                           
   .If <bit [SI].DDFlags z SecondaryKey>   ; If a EO scan DID NOT precede Scan
     .If <bit [SI].Key.Shift z NumTogl>    ; If NumLock toggled OFF
       .If <bit [SI].Key.Shift nz LShiftFlag+RShiftFlag> ; If a shift key down
          Nop                              ; Nop, Don't OR in ShiftMask
       .Else                               ; Shift key not down
          Or [SI].DDFlags,ShiftMask        ; Turn DD Shift Flag ON
       .Endif                              ; Endif
     .Else                                 ; Else, NumLock toggled ON
       .If <bit [SI].Key.Shift nz LShiftFlag+RShiftFlag> ; If a shift key down
          Or [SI].DDFlags,ShiftMask        ; Turn DD Shift Flag ON
       .Endif                              ; Endif
     .Endif                                ; Endif NumLock OFF
                                           
   .Else                                   ; Else E0 did precede, (extended insert)
       Or [SI].DDFlags,ShiftMask           ; Turn DD Shift Flag ON
   .Endif                                  ; 
                                           ; 
 .Endif                                    

                                           
  Push   DS                                ; Save VDD Data sel
  Mov    DS, StkDS                         ; Get PKBDs data sel
 .If <NumPadAccum ne 0>                    ; If not 0, We need to send
                                           ; the Accumulation packet for a R-Alt.
    Mov   AL, NumPadAccum                  ; Get the accumulation value
    Mov   NumPadAccum, 0                   ; clear value
    Pop   DS                               ; Get VDD Data sel
    Mov   [SI].Key.XChar, AL               ; Put it in the Keypacket
    Mov   [SI].DDFlags, 0                  ; Clear DDFlags
 .Else                                     ; Else
   Pop DS                                  ; Get VDD Data sel
 .Endif                                    ; Endif

  Mov    DX,[SI].Key.Shift                 ; Get current shift state
  And    DX, Not HKResMask                 ; Turn off unrelated bits
  Push   DS
  Pop    ES
  Mov    DS, StkDS                         ; Get PKBDs data sel
  Mov    BX,Offset HotKeyTable             ; Get Hot Key table address

      ;* DX = Current Shift status    CX = HotKey Shift status
      ;* Check for HotKey shift state only

  Xor AH,AH                                   ; Initialize loop counter to zero
  .Repeat

     Mov CX,[BX].HotKeyShift                  ; Hot Key Shift State
     .If <DX eq CX> OR                        ; If shift state is exactly alike OR
     .If <bit DX nz CX> AND                   ; A HotKey shift keys is down  AND
       Not CX                                 ; Turn off HotKey shift bits
     .If <bit DX z CX>                        ; No non HotKey shift Keys are down

        .If <AL eq [BX].HotKeyMake>           ; If this scan is a HotKey make
           .If <ES:[SI].DDFlags eq WakeUpKey> ; If we thought this was a
              Or ES:[DI].XlateFlags, SGPaused ; WakeUpKey we must signal that
           .Endif                             ; it is not
           Mov ES:[SI].DDFlags,KeyTypeMask    ; Update the device driver flags
           Mov AH,1                           ; No more checking
        .Elseif <AL eq [BX].HotKeyBreak>      ; If this scan is a HotKey break
           Mov ES:[SI].DDFlags,KeyBreak+HotKeyPacket ; Update the device driver flags
           Mov AH,1                           ; No more checking
        .Endif

     .Endif

     Inc AH                                   ; Increment loop counter
     Add BX,HKEntrySize                       ; Get next HotKey entry
; .Until <AH eq MaxHotKeyCt>                  ; BUGBUG JOECELI
  .Until <AH gt 1>


  Ret

VDMScanFilter Endp



Public Layer_HKChk
Layer_HKChk Proc

;*********************************************************************
;*
;* FUNCTION NAME   Layer_HKChk
;*
;* DESCRIPTION     Set proper index for switching between the
;*                 Primary and Secondary Layers.
;*
;*                 Called by HotKeyCheck. If a Multi Layer Hot Key
;*                 sequence is made, the routine will set up the
;*                 CPCB index to allow for switching between the
;*                 Primary or Secondary layer of support, depending
;*                 on which layer was active previously.
;*
;*                 This routine assumes that multilayer codepages
;*                 are supported.
;*
;*
;* INPUT           DI = Ptr to current PSG
;*                 SI = Ptr to keypacket
;*
;* RETURN NORMAL   Return to caller of routine.
;*
;* RETURN ERROR    None.  All exits are through NORMAL above.
;*
;* OUTPUT         If a Multi-Layer Hot Key occured, the CPCB
;*                index in the current KCB will be changed.
;*                AH, ES, DH altered.
;*
;* INTERNAL REFERENCES   AccessKCB
;*
;*
;*
;* EXTERNAL REFERENCES   NONE
;*
;********************************************************************
                                            
  Mov AH,Byte Ptr [SI].KPacketLen+1         ; Get scan code in AH
 .IF <AH eq LSBreak> OR                     ; If scan code is L Shift Key Break OR
 .IF <AH eq RSBreak>                        ; If scan code is R Shift Key Break
   Push BX                                  ; Save BX
   Mov BX, [SI].Key.Shift                   ; Put the shift state into BX
   And BX, NOT CapsTogl+NumTogl+ScrollTogl  ; Mask off Caps,Num,and Scroll states
    .IF <BX eq AltFlag+LAltFlag> OR         ; If ONLY the Left Alt key is down
    .IF <BX eq AltFlag+RAltFlag>            ; If ONLY the Right Alt key is down
        Push DI                             ; Save PSG pointer
        Mov DH, ES_DI                       ; Specify regs. for next call
        Call AccessKCB                      ; ES:DI = seg/sel:off of KCB
       .IF <AH eq LSBreak>                  ; If scan code break of left Shift
          Mov ES:[DI].KCBIxOffCP, Index1    ; Update KCB index to use Primary layer
       .Else                                ; Scan code is break of right shift
          Mov ES:[DI].KCBIxOffCP, Index3    ; Update KCB index to use Secondary layer
       .Endif                               ; Endif scan code break of left Shift
        Pop DI                              ; Restor PSG pointer
    .Endif                                  ; Endif ONLY the Alt key is down
   Pop BX                                   ; Restore BX
 .Endif                                     ; Endif scan code is L or R Shift key brk
  Ret                                       ; Return to caller
                                            
layer_HKChk endp



BREAK <Typa_RDCheck subroutine>
Public Typa_RDCheck
Procedure Typa_RDCheck, HYBRID

;**********************************************************************
;*
;*  FUNCTION     Typa_RDCheck
;*
;*  DESCRIPTION  Check the rate and delay values and put
;*               them into the appropriate format for the
;*               keyboard.
;*
;*               This routine checks the rate and delay values that
;*               were passed in SI and AX respectively.  It deter-
;*               mines whether these parameters are appropriate
;*               for setting the keyboard and adjusts them accord-
;*               ingly if not.  Before returning it places the rate
;*               and delay values into AX.
;*
;*  NOTE         This routine is called by either Cat. 4 IOCTL 54h or
;*               by MVDM interface function SetRepeateRate.
;*
;*
;*  INPUT          SI = rate parm.
;*                 AX = delay parm.
;*
;*  OUTPUT         AX has Rate and Delay values in it.
;*
;*  RETURN NORMAL  SI is preserved.  AX is NOT preserved.
;*
;*  RETURN ERROR   None.
;*
;*  EFFECTS   Rate and Delay values may be adjusted and then are
;*           placed in AX.
;*
;*****************************************************************

 .If <AX ge 1000>                       ; If greater then or equal to 1000ms
    Mov    AH,3                         ; Use 1000ms as the delay
 .Elseif <AX le 250>                    ; If less then or equal to 250ms
    Xor    AH,AH                        ; Use 250ms as the delay
 .Else                                  ; It is in the valid range
    Mov    DL,250                       ; Divide milliseconds by 250.
    Div    DL
    Dec    AL                           ; Zero based
    Mov    AH,AL
 .Endif

 .If <SI ge 30>                         ; If 30 cps or greater
    Xor    AL,AL                        ; Use 30 cps (max rate)
 .Elseif <SI le 2>                      ; If 2 cps or less
    Mov    AL,1fh                       ; Use 2 cps (min rate)
 .Else                                  ; In valid range
    Mov    AL,[TypaRates+SI]            ; Get bit pattern for it
 .Endif

  Ret

EndProc Typa_RDCheck

BREAK <KHBuffer subroutine>
Public KHBuffer
KHBuffer Proc

                                  ;             BEGIN
;*********************************************************************
;* FUNCTION NAME   KHBuffer
;*
;* DESCRIPTION     keep a buffer of scan code MAKES for which BREAKS
;*                 have not yet occurred.
;*
;*                 This routine stores the MAKE scan codes until it's
;*                 corresponding BREAK occurs.  When that happens the
;*                 MAKE is removed.  After all keystrokes are done, (make
;*                 and break) the buffer should be empty, with all zero's.                                 *
;*                 If a SG switch occurs before the buffer is emptied, then
;*                 the SCCount will be non-zero and the buffer will have
;*                 the remaining MAKE scans in it for which BREAKS must
;*                 occur.  IOCTL 55, SetCurrentSG will be blocked until
;*                 the MAKEs occur at interrupt time. When the buffer is
;*                 empty then the block will be run.
;*
;*  INPUT
;*                AL = Raw Scan Code, AH = Scan Code w/ Break bit cleared
;*
;*  RETURN NORMAL Registers are not affected
;*
;*  RETURN ERROR  None
;*
;*  OUTPUT        Buffer is updated
;*
;********************************************************************

  Push AX                           ; Save Regs

  Push BX                           ; Save Regs
  Push DI                           ; Save Regs
  Test AL,80h                       ; Check if this is a key break.
 .If <z>                            ; If not, attempt to add scan code to KeyHistory.
   .If <SCCount lt 9>
      Mov BX, 9                     ; Set BX to maximum # of elements in buffer
      Mov DI,0                      ; Move in value of first buffer element
      AND AIMFlags, 7FFFh           ; Match flag starts as OFF
     .Repeat                        ; Look for scan code in KeyHistory.
        .If <AH ne [DI+KeyHistBuf]> ; Check if repeat of last key make in this SG
           Inc DI                   ; If NOT match then move to next element
        .Else                       ; Else scan code is repeat of key make
           Or AIMFlags, 8000h       ; Found match so set Match flag to ON
           Mov DI, 9                ; Set DI to equal BX to get out of loop NOW
        .Endif
     .Until <DI eq BX>              ; Repeat at most until all elements have been checked
     Push AX                        ; Save Scan Code during following.
     Test AIMFLAGS, 8000h           ; Check if Match was found
     .If <z>                        ; If match NOT found then ADD scan code to buffer
        Inc SCCount                 ; increment the scan code count
        Xchg KeyHistBuf,AH          ; Isn't, so put new scan in KeyHistory.
        Xchg KeyHistBuf+1,AH        ; And move
        Xchg KeyHistBuf+2,AH        ;   the other
        Xchg KeyHistBuf+3,AH        ;      keys back
        Xchg KeyHistBuf+4,AH        ;         in the queue.
        Xchg KeyHistBuf+5,AH        ;         in the queue.
        Xchg KeyHistBuf+6,AH        ;         in the queue.
        Xchg KeyHistBuf+7,AH        ;         in the queue.
        Xchg KeyHistBuf+8,AH        ;         in the queue.
        Xchg KeyHistBuf+9,AH        ;         in the queue.
     .Endif                         ; 
      Pop AX                        ; Restore Scan Code.
   .Endif
 .Elseif <SCCount ne 0>             ;                a key break, so check the KeyHistory.
    Xor BX,BX                       ; Get a zero offset.
   .Repeat                          ; Look for scan code in KeyHistory.
      Mov DL,Byte Ptr[BX+KeyHistBuf]; Get nexistory entry.
     .If <AH eq DL>                 ; Is scan code the make for our current break?
          Dec SCCount               ; decrement the scan code count
          Xor AH,AH                 ; Clear out AH
          Mov DI,9                  ; Move in value of highest buffer element
          Dec BX                    ; adjust BX to make the buffer swaps correct
         .Repeat
            Xchg Byte Ptr[DI+KeyHistBuf], AH   ; remove proper scan from buffer.
            Dec DI                  ; Dec index to next buffer element
         .Until <DI eq BX>          ; Repeat until we reach element to remove
          Mov BX,9                  ;            Yes, so set quit condition.
     .Endif                         ; Inc the index to buffer elements
      Inc BX                        ; Point to next entry in KeyHistory.
   .Until <BX eq 0Ah>               ; Repeat at most until all elements have been checked
 .Endif                             ; 
  Pop DI                            ; Restore
  Pop BX                            ;      the
  Pop AX                            ;        regs
 .If <AL eq 0FFh>                   ;  If scan code is a FF, Either a Key Detection
                                    ;  error occured in Hardware or a HW overrun.
                                    ;  In either case we must clear out the SCCount
                                    ;  and the KeyHistBuf since breaks won't happen.
    Mov SCCount,0                   ;  Clear out the SCCount
    Mov  KeyHistBuf, 0              ;  Clear out the KeyHistBuf
    Mov  KeyHistBuf+1,0             ;  Clear out the KeyHistBuf
    Mov  KeyHistBuf+2,0             ;  Clear out the KeyHistBuf
    Mov  KeyHistBuf+3,0             ;  Clear out the KeyHistBuf
    Mov  KeyHistBuf+4,0             ;  Clear out the KeyHistBuf
    Mov  KeyHistBuf+5,0             ;  Clear out the KeyHistBuf
    Mov  KeyHistBuf+6,0             ;  Clear out the KeyHistBuf
    Mov  KeyHistBuf+7,0             ;  Clear out the KeyHistBuf
    Mov  KeyHistBuf+8,0             ;  Clear out the KeyHistBuf
    Mov  KeyHistBuf+9,0             ;  Clear out the KeyHistBuf
 .Endif                             ;  Endif

  Ret                               ; 
                                    ; End PTR B786969            
KHBuffer Endp

BREAK <SGActivation subroutine>
Public SGActivation
Procedure SGActivation, HYBRID

;*************** START OF SPECIFICATIONS *****************************
;*
;* FUNCTION NAME  SGActivation
;*
;* DESCRIPTION    Checks for and sets the bits in the SGCreatedTbl.
;*
;* FUNCTION:
;*              Called by various routines to either query a session's
;*              activation bit or to set the corresponding session bit
;*              in the SGCreatedTbl.  Activation bits in the SG-
;*              CreatedTbl are set as follows:
;*
;*              For VDM SG's (Currently anything above 15)
;*
;*                Bit Set if VDMCreated has been received for the SG
;*                Bit Clear if no VDMCreated received or VDMTerminate
;*                    has been received for the SG
;*
;*                For Non VDM SG's (Currently 0-15)
;*
;*                Bit Set if SG becomes foreground session or an IOCTL
;*                    is received for the SG (session has a PSG)
;*                Bit Clear if no functions have been executed for the
;*                    SG, or if a termination has field for the SG in
;*                    the SetCurrentSG IOCTL.
;*
;* NOTE         This subroutine is being added for CP20 DCR 549.
;*
;*
;* INPUT
;*           AX = Screen number to use
;*
;*           BL = Function indicator
;*                0 - Clear CurSG's activation bit
;*                1 - Set CurSG's activation bit
;*               -1 - Query the CurSG's activation bit
;*
;*           DS = PKBD DS
;*
;*
;* RETURN NORMAL Carry set if CurSG's activation bit is set in the
;*               SGCreatedTbl.
;*               Carry clear if the CurSG's activation bit is off in
;*               SGCreatedTbl.
;*
;* RETURN ERROR  None.  All exits are through NORMAL above.
;*
;* OUTPUT       Alters BX,AX
;*
;*
;*************** END OF SPECIFICATIONS *******************************

                                        
    SaveReg <AX,DX,SI,CX>               ; Save 'em, we use 'em.
    Mov  DL,8                           ; 8 is scaling factor for table.
    Div  DL                             ; Divide CurSG by scaling factor
    Mov  DX,1                           ; Setup a single bit mask.
    Mov  CL,AH                          ; Put the remainder into CL.
    Mov  AH,0                           ; (BX)=VDM mask for SGCreatedTbl.
    Shl  DX,CL                          ; Mov offset for table into indx reg.
    Mov  SI,AX                          ; DX holds bit mask for checking or
                                        ; setting the SGCreatedTbl.
                                        ; SI holds the offset into the table.
    Mov  AX, Word Ptr SGCreatedTbl[SI]  ; Get the table contents at offset.
                                        ; 
   .If <BL eq 1>                        ; If the function is to Activate
                                        ; then turn the bit on in the
      Or   Word Ptr SGCreatedTbl[SI],DX ; table for the given session.
      Stc                               ; Set carry to indicate activated.
                                        ; 
   .Elseif <BL eq 0>                    ; ElseIf the function is to De-
                                        ; activate, then turn the bit off
      Not  DX                           ; for the given session.
      And  AX,DX                        ; Use mask to clear table bit.
      Mov  Word Ptr SGCreatedTbl[SI],AX ; Clear bit in table.
      Clc                               ; Carry is cleared for not activated
                                        ; 
   .Elseif <bit DX nz AX>               ; Else this is a query so check to
      Stc                               ; see if the given session's bit is
   .Endif                               ; on. Set carry if it is.
                                        ; 
    RestoreReg <CX,SI,DX,AX>            ; Restore 'em.
    Ret                                 ; Bye!

EndProc SGActivation

Code Ends
     End
