;*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   KBDIOCG - IOCTL "Get" Routines
    Name    KBDIOCG

;/*****************************************************************************
;*
;* SOURCE FILE NAME = KBDIOCG.ASM
;*
;* DESCRIPTIVE NAME = PKBD IOCTL Get Routines
;*
;*
;* VERSION      V2.0
;*
;* DATE         17/03/92
;*
;* DESCRIPTION  This file contains the subroutines that handle the GET
;*              IOCTLs.
;*
;* NOTES
;*    DEPENDENCIES:  A valid Request Packet is sent.
;*    RESTRICTIONS:  Machine must be a 386 or compatible with a 386.
;*
;* FUNCTIONS         GetInputMode
;*                   GetICFlags
;*                   GetShiftState
;*                   XReadOrPeek
;*                   GetSMHotKey
;*                   GetKeyboardType
;*                   GetInUseCP
;*                   XlateSC
;*                   GetKbdHWID
;*                   GetKbdCPInfo
;*
;*
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   UNKNOWN               652    Setup to use local variables on stack
;*   UNKNOWN               1388   Setup to use local variables on stack
;*   UNKNOWN               3829   UNKNOWN
;*   UNKNOWN               3117/3377
;*   UNKNOWN               4002
;*                         4480,4581
;*                         3683
;*                         3431
;**************************************************************************
.286p
.xcref
.sall
       ;* The following files are included, but XLIST'd:
.xlist
  Include basemaca.inc   ;DOS macros.
  Include osmaca.inc     ;Macro file for OS/2 kernel
  CPUMODE 286            ; 
  Include struc.inc      ;Structured assembly macros.
  Include devhlp.inc     ;DevHlp & Signal equates, char queue structure.
  Include devsym.inc     ;Device driver header definitions.
  Include kbdseg.inc     ;Segment definitions.
  Include kbdxlat.inc    ;Translate table structure definitions/macros.
  Include kbdddr.inc     ;Keyboard Device Driver structures & equates.
  Include kbddi.inc

.list

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

StkFrameVars                   ; Macro to set up proper equates.

;External Routines
    EXTRNFAR AccessKCB         ;           
    EXTRNFAR BlockCheck        ;           
    EXTRNFAR UnBlockCheck      ;           
    EXTRNFAR DiscardElement    ;           
    EXTRNFAR KbdXlate          ;           
    EXTRNFAR ProcessScanCode
    Extrn YieldProc            :Near
    Extrn FirstReadCheck       :Near
    Extrn IOCtlAddrCheck       :Near


;External Variables
    Extrn XDRSIZE              :ABS
    Extrn XDRCharData          :Byte
    Extrn XDRFlagArea          :Byte
    Extrn HotKeyTable          :Byte
    Extrn HotKeyCount          :Byte
    Extrn MiscFlags2           :Byte
    Extrn CmdFlags             :Byte
    Extrn MiscFlags            :Byte
    Extrn CurSG                :Word
    Extrn KeyboardType         :Word
    Extrn KbdHWIDs             :Word
    Extrn CPCount              :Word
    Extrn XDRPacket            :Word
    Extrn NotifyPct            :Word
    Extrn CBDataSel            :Word
    Extrn KeyPacket2           :Word
    Extrn DeviceHelp           :DWord
    Extrn ExExTable            :Byte    ;           
    Extrn ExTable              :Byte    ;           
    Extrn CheckKBDDD           :WORD
    Extrn    IDC_CB            :WORD


BREAK <Kbd IOCTL Processing Note>

;***********************************************************************
;*            IOCTL REQUEST PROCESSING ROUTINES FOLLOW
;***********************************************************************
;*  Note, on entry to the following routines, the following regs are:
;*
;*      AX = SG that caller is running in.      DI = PSG address
;*   ES:BX = Ptr to Request Block
;*      DL = Function number being called.
;*      DH = 1 = Read/Write access parm for verification of caller's
;*           data areas
;*      CX = Zero (most likely value for parm list addr validation call)
;*
;*
;*  In addition the strategy routine's stack contains the following
;*  variables (all variables are prefaced with "Stk" in the code):
;*
;*      - DD's data segment/selector.
;*      - Caller's PSG offset.
;*      - Selector to KIB/CPCB data area
;*      - Caller's KCB selector
;*      - Caller's KCB offset. An LES/LDS instruction can be used here.
;*      - Caller's Request Packet selector/segment.
;*      - Caller's Request Packet offset. An LES/LDS inst. can be used.
;*      - Caller's Process ID.
;*
;*  Be sure not to alter BP (without restoring), so that addressability
;*  to the stack variables is maintained.
;*
;***********************************************************************

StratCode Segment  ;           ***\

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

BREAK <IOCTL 71h - Get Input Mode>
Public GetInputMode
GetInputMode Proc Far


;/***************************************************************************
;*
;* FUNCTION NAME = GetInputMode
;*
;* DESCRIPTION   = This routine first ensures that the IOCTL caller's
;*                 data area can be written to.  If so, then the input
;*                 mode byte is retrieved from the current KCB and
;*                 placed into the IOCTL caller's Data Area.
;*
;*                 The current KCB is the KCB which is active for the
;*                 caller's session.
;*
;*
;* INPUT         = REGISTERS:
;*
;*                   ES:BX = PTR TO REQUEST BLOCK
;*                      DI = PSDA ADDRESS
;*
;*                      THE CURRENT KCB HAS THE BYTE TO BE COPIED INTO THE
;*                      IOCTL CALLER'S DATA BUFFER.
;*
;*
;* OUTPUT        = SI, AL are not preserved.
;*                 The IOCTL caller's data byte is updated
;*
;* RETURN-NORMAL = Return to main Strategy processing.  The IOCTL
;*                 caller's data area is modified.
;*
;* RETURN-ERROR  = Same.  IOCtlAddrCheck may set an error in the reques
;*                 packet.
;*
;**************************************************************************/
  Xor CX,CX                             ; No parameter packet
  Mov SI,1                              ; Set return data length for verify.
  Call IOCtlAddrCheck                   ; Go check it.
 .If < nc >                             ;            No carry means
                                        ; valid parm,
       LES    SI, Dword Ptr StkKCBO     ; Get address of caller's KCB.
       Mov    AL, ES:[SI].KCBInpMode    ; Get Input Mode byte from the
                                        ; caller's KCB.
       Mov    ES, StkRPS                ; Restore request packet seg.
       LES    SI, ES:[BX+DataBfrPtr]    ; Get address of data buffer.
       Mov    ES:[SI],AL                ; Pass input mode to caller.

    Mov ES, StkRPS                      ; Restore request packet seg.
 .Endif                                 ; End valid parm check.
  Ret

GetInputMode Endp

BREAK <IOCTL 72h - Get Interim Character (DBCS) Flags>
Public GetICFlags
GetICFlags Proc Far


;/***************************************************************************
;*
;* FUNCTION NAME = GetICFlags
;*
;* DESCRIPTION   = Obtain the Interim Character Flags byte from
;*                 the KCB for the caller's session and return it to
;*                 the IOCTL Caller.
;*
;*                 This routine first ensures that the IOCTL caller's
;*                 data area can be written to.  If so, then the IC
;*                 flag byte is retrieved from the KCB and is placed
;*                 into the IOCTL caller's Data Area.
;*
;*
;* INPUT         = REGISTERS:
;*
;*                   ES:BX = Ptr to Request Block
;*                      DI = PSDA address
;*
;*                      The PSDA contains the byte to be copied into the
;*                      IOCTL caller's Data Buffer.
;*
;*
;* OUTPUT        = SI, AL are not preserved.
;*                 The IOCTL caller's data byte is updated
;*
;* RETURN-NORMAL = Return to main Strategy processing.  The IOCTL
;*                 caller's data area is modified.
;*
;* RETURN-ERROR  = Same.  IOCtlAddrCheck may set an error in the reque
;*                 packet.
;*
;**************************************************************************/
  Xor CX,CX
  Mov SI,1                              ; Set return data length for verify.
  Call IOCtlAddrCheck                   ; Go check it.

 .If < nc >                             ;            No carry means
                                        ; valid parm,
    Mov ES, StkKCBS                     ; Get the address of the KCB
    Mov SI, StkKCBO                     ; for the caller's session.
    Mov AL, ES:[SI].KCBDBCSFl           ; Get DBCS flags from the KCB.
    And AL, Not ICResMask               ;            Show only
                                        ; the allowed bits.
    Mov ES, StkRPS                      ; Restore request packet seg.
    LES SI,ES:[BX+DataBfrPtr]           ; Get address of data buffer.
    Mov ES:[SI],AL                      ; Pass to caller.

    Mov ES, StkRPS                      ; Restore request packet seg.
 .Endif                                 ; End valid parm check.
  Ret

GetICFlags Endp

BREAK <IOCTL 73h - Get Shift State>
Public GetShiftState
GetShiftState Proc Far


;/***************************************************************************
;*
;* FUNCTION NAME = GetShiftState
;*
;* DESCRIPTION   = Obtain the Shift and DBCS Shift State flags
;*                 from the caller's KCB and returns them to the IOCTL
;*                 caller.
;*
;*                 This routine first ensures that the IOCTL caller's
;*                 data area can be written to.  If so, then the desired
;*                 shift states flag words are retrieved from the
;*                 caller's Keyboard Control Block and placed into the
;*                 the IOCTL caller's Data Area.
;*
;*
;*
;* INPUT         = REGISTERS:
;*
;*                   ES:BX = Ptr to Request Block
;*                      DI = PSDA address
;*
;* OUTPUT        = SI, AX are not preserved.
;*                 The IOCTL caller's data area is updated
;*
;* RETURN-NORMAL = Return to main Strategy processing.  The IOCTL
;*                 caller's data area is modified.
;*
;* RETURN-ERROR  = Same.  IOCtlAddrCheck may set an error in the request
;*                 packet.
;*
;**************************************************************************/

  Xor CX,CX                             ; No parameter packet
  Mov SI,3                              ; Set return data length for verify.
  Call IOCtlAddrCheck                   ; Go check it.

 .If < nc >                             ; No carry means
                                        ; valid parm,
    Push DX                             ; Save caller's register.
    Mov ES, StkKCBS                     ; Get the address of the KCB
    Mov SI, StkKCBO                     ; for the caller's session.
    Mov AX,ES:[SI].KCBShift             ; Get shift flags from KCB.
    Mov DL,ES:[SI].KCBDBCSSh            ; Get DBCS shift flags from
                                        ; the KCB.
    Mov ES, StkRPS                      ; Restore request packet seg.
    LES SI,ES:[BX+DataBfrPtr]           ; Get address of data buffer.
    Mov ES:[SI],AX                      ; Pass to caller.
    Mov ES:[SI+2], DL                   ; Pass DBCS shift flags to
                                        ; the caller.
    Pop DX                              ; Restore caller's register.
    Mov ES, StkRPS                      ; Restore request packet seg.

 .Endif                                 ; End valid parm check.
  Ret

GetShiftState Endp

BREAK <IOCTL 74h/75h - Extended Read/Peek of a character>
Public XReadOrPeek
XReadOrPeek Proc Far


;/***************************************************************************
;*
;* FUNCTION NAME = XReadOrPeek
;*
;* DESCRIPTION   = Return one or more CharDataRecs to the IOCTL
;*                 caller's data area and optionally remove the
;*                 CharDataRec from the KIB.
;*
;*                 This routine first ensures that the IOCTL caller's
;*                 data area can be written to.  If so, then the
;*                 desired number of CharDataRecs is transferred from
;*                 the KIB to the IOCTL caller's data buffer (via a
;*                 pointer in the Request Packet).  If the function was
;*                 a READ then the KIB is depleted accordingly.  The
;*                 number of actual records transferred is also
;*                 returned to the IOCTL caller, as well as the input
;*                 mode flag (indicating ASCII or Binary mode).  Any
;*                 Shift Report records (which have been placed into the
;*                 buffer by the interrupt handler) transferred to the
;*                 caller.
;*
;*                 The KIB In and Out pointers and count of elements in
;*                 the KIB are in the current KCB.
;*
;*                 If a thread exists ahead of this one which is
;*                 utilizing the KIB, then this thread will block
;*                 until the previous thread has completed.  Similarly,
;*                 this thread, upon completion of processing, will
;*                 start any following thread which is awaiting access
;*                 to the KIB.
;*
;*                 If there are no records available and the IOCTL
;*                 wishes to WAIT (signified by the a 0 in the high
;*                 order bit of the transfer count field), then the
;*                 thread will Block until a character (data record)
;*                 is available.
;*
;*                 If Read or Peek Notification has been enabled for
;*                 the screen group of this IOCTL, and there are monitors
;*                 in the chain, then a special monitor notification
;*                 packet will be sent down the monitor chain.
;*
;* NOTES         = Block Check moved to beginning of routine to
;*                 facilitate parm checking.
;*
;* INPUT         = REGISTERS:
;*
;*                   ES:BX = Ptr to Request Block
;*                   DL    = Function number being called.
;*
;*                 The caller's parm list area contains a word indicating
;*                 the number of records to transfer, with the sign bit
;*                 indicating whether to wait (0) or not (1).
;*
;* OUTPUT        = SI, AX, and BP are not preserved.
;*                 The IOCTL caller's data buffer is filled with the
;*                 desired number of CharDataRecs.
;*                 The Parm List word contains the number of records
;*                 actually transferred, with the sign indicating
;*                 ASCII (0) or Binary (1) mode.
;*
;*
;* RETURN-NORMAL = Return to main Strategy processing.  The IOCTL
;*                 caller's data area is modified.
;*
;* RETURN-ERROR  = Same.  IOCtlAddrCheck may set an error in the reques
;*                 packet.
;*                 Unknown command error returned if in Single Queue
;*                 mode.
;*
;**************************************************************************/

  Test MiscFlags,NoReadYet               ; Check if first read/peek from kbd.
 .If <nz>                                ; Is it?
    Call   FirstReadCheck                ; Yes check if ok to start processing
 .Endif                                  ; keys.
;;
;; This code is to take care of the keystrokes before the KBDDD is loaded.
;;
 .If <CheckKBDDD eq 0>                      ; Has there been a device registered
    push si                                 ; Save registers
    push cx
    Xor  cx,cx                              ; Clear counter register
    Lea  si,IDC_CB                          ; Get device dependent registry
   .While <cx lt MAXIDCS>                   ; Look though all five of them
     .If <bit [si].IDC_Flags nz IDC_ACTIVE> ; If handle is active.
         Mov CheckKBDDD, 0001h              ; Then we have one registered
     .Endif
      inc cx                                ; Increment counter
      add si, size IDC_Entry                ; Increment registry
   .Endwhile
   .If <CheckKBDDD eq 0>                    ; If no one is registered
      pusha                                 ; Save registers
      Mov ax, 001Ch                         ; Send an ENTER make
      CALLFAR ProcessScanCode               ; Process it
      Mov ax, 009Ch                         ; Send an ENTER break
      CALLFAR ProcessScanCode               ; Process it
      popa                                  ; Restore registers
   .Endif
   pop cx                                   ; Restore register
   pop si                                   ; Restore register
 .Endif
;;
;; End of added code
;;
  Test [DI].PSGFlags, SQMODE             ; If in Single Queue
 .If < nz >                              ; Mode, indicate unknown
    LES   BX, Dword Ptr StkRPO           ; Restore request packet address.
    Mov   ES:[BX+PktStatus],CmdError     ; Indicate invalid parameter in RP
    Ret                                  ; Get Out Now.
 .Endif                                  ; End if in Single Queue mode.

  Push    DX                             ;            Save read/peek ind.
  CALLFAR BlockCheck ; Check block because someone's ahead. se someone's ahead.
  Pop     DX                             ;            Restore read/peek ind.
                                         ;           
  Test ES:[BX].PktStatus, STERR          ; If error occurred during
 .If < nz >                              ;  BlockCheck,
    Ret                                  ; Get Out Now.
 .Endif                                  ; End if BlockCheck error.
                                         ;            BEGIN
  Mov    CX,2                            ; Set length for parm check.
  Xor    SI,SI                           ; No data buffer parm checking.
  Call   IOCtlAddrCheck                  ; Verify read access to table.
 .If < c >                               ; Carry set = access denied.
    Ret                                  ; Get Out Now.
 .Endif                                  ;            END:

  Push   ES                              ; Save request block seg.
  LES    SI,ES:[BX+ParmListPtr]          ; Get address of parm list.
  Mov    SI,ES:[SI]                      ; Fetch number of records requested.
  IMul   SI,ChDaRecLen                 ; Calc required buffer length for verify.
  Xor    CX,CX                           ; Set parm list length for verify.
  Pop    ES                            ; Fix stack since next call may not return.
  Call   IOCtlAddrCheck                  ; Go check it.
 .If < c >                               
                                         ; Invalid Parms,
    CALLFAR UnBlockCheck                 ; Start any queued thread.
    Ret                                  ; Get Out Now.
 .Endif                                  ; End invalid parms check.

  Push   ES                              ; Save request block seg again.
  LES    SI,ES:[BX+ParmListPtr]          ; Get address of parm list.
 .If <DL eq 75h>                         ; If Extended peek...
    Mov  DL,XPeek                        ; Set peek bit in option byte.
    Mov  CX,8001h                        ; Set Xfer count for X Peek + NOWAIT.
 .Else
    Xor  DL,DL                           ; Else indicate regular read.
    Mov  CX, Word Ptr ES:[SI]            ; Get Xfer count from parm list.
 .Endif
  Or     CX,CX             ;Typematic Rate/Delay and Acceptance Rate Control
  Push   SI                ; Check NoWait bit on in transfer count
  Mov    SI,Offset DGROUP:NotifyPct      ; Point to Key Packet.
  Mov    Byte Ptr [SI].Option.ReqType, 0 ; clear nowait in Read/Peek Packet.
  Pop    SI                              ; 
 .If <s>                                 ; Is it?
    Or   DL,NoWait                     ; Yes, so set NoWait bit in option byte.
    And  CX,7FFFh                      ; Clear NoWait bit in word count.
    Mov  Word Ptr ES:[SI],CX           ; Put it back into request parmlist.
    Push SI                            ; 
    Mov  SI,Offset DGROUP:NotifyPct      ; Point to Key Packet.
    Mov  Byte Ptr [SI].Option.ReqType, 1 ; Ind. nowait in Read/Peek Not. Packet.
    Pop  SI                              ; 
 .Endif
  Pop    ES                              ; Restore Request Block pointer.
  Push   CX                              ; save word count from destruction.
  Push   DX                              ; save option byte from destruction.
  Test Word Ptr [DI].NotifyFlags, SGEnabled ; Check if Read or Peek Not. is on
 .If < nz >  AND                         ; for this SG,  if so AND
  And [DI].NotifyFlags, NOT NotPctSent   ; Reset flag saying that we sent Not.
                                         ; Packet down the monitor chain.
 .If <[DI].MonsInstalled gt 0>           ; If mons registered, then


   Push SI                               ; 
   Mov  SI,Offset DGROUP:NotifyPct       ; Point to Notify Monitor KeyPacket.
   Mov  AX, CurSG                        ; Get the current screen group num.
   Mov  Word Ptr [SI].Option.ScrGrp, AX  ; Put requesting SG# in packet.
   Mov  Word Ptr [SI].NDDFlags, XRorPNot ; Indicate to Monitors, KBDDD
                                         ; this is a Read or Peek Not. Packet.
                                         
   SaveReg<es,di>                        ; DI points to caller's PSG
   Mov  DH, ES_DI                        ; Specify registers for KCB addr.
   CALLFAR AccessKCB                     ; ES:DI=Virt addr. of caller's KCB.
   mov  ax, es:[di].KCBOutPtr            ; OutPtr may indicate empty buffer.
    RestoreReg<di,es>                    
    Mov Word Ptr [SI].Option.Buffer, AX  ; Store this in the packet.
    Mov AX, [DI].MonHandle               ; Get Monitor Chain handle.
    Mov CX, KeyPacketLen                 ; Set packet length
    Mov DX, 0100h + DevHlp_MonWrite      ; NoWait for Monitor accept and func.
    Call [DeviceHelp]                    ; Pass keypacket to monitors.
   .If < nc >
       Or [DI].NotifyFlags, NotPctSent   ; Indicate that Mon. Dispatcher
                                         ; accepted the Notification Packet.
   .Endif                                ; Endif Monitor Disp accepted Pac.
    Pop SI                               ; 
 .Endif                                  ; Endif RorP Not. AND Mons. register.
  Mov SI,DI                              ; Put PSG pointer in SI for the following.

  Mov AX, ES:[BX+DataBfrPtr]             ; Get the address of the
  Mov BX, ES:[BX+DataBfrPtr+2]           ;  caller's data buffer.

  Mov DH, ES_DI                          ; Specify registers to get KCB addr.
                                         ; (DI already points to caller's PSG)
  CALLFAR AccessKCB                      ; ES:DI = Virt. addr. of caller's KCB.
  Mov Word Ptr ES:[DI].KCBDataBfrPtr, AX ; Save away caller's
  Mov Word Ptr ES:[DI].KCBDataBfrPtr+2, BX ;  data buffer addr.
  Pop DX                                 ; Restore the (no)wait indicator.
  Pop CX                                 ; Restore the transfer count.

  Cli                                    ; Don't allow interrupts for a few.

 .While <CX gt 0> AND NEAR               ; Xfered all requested chars/records?
 .While <ES:[DI].KCBOutPtr ne -1 > OR    ; No, any records in the KIB?
  Test DL,NoWait                         ; If not, check if okay to wait.
 .While <z> NEAR                         ; Can we?

    ;Continue this loop if recs are available, or if okay to wait.

    Push CX                              ; Save the transfer count .
    Cli                            ; Turn off interrupts while making next check.
   .While <ES:[DI].KCBOutPtr e -1>       ; KIB empty & okay to wait?
      Mov  BX,SI
      Add  BX,EventID                    ; Set AX:BX to address...
      Mov  AX,DS                         ;  ...of EventID buffer.
      Mov  DS:[BX],BX                    ; Put Blocked Event ID where...
      Mov  DS:[BX+2],AX                  ; ..int processor will see it.
      Push DX
      Mov  CX,-1                         ; Indicate to...
      Mov  DI,CX                         ; ...never timeout.
      Xor  DH,DH                         ; Indicate okay to interrupt.
      Mov  DL,DevHlp_ProcBlock           ; Set ProcBlock DevHlp function.
      Call [DeviceHelp]                  ; Go block the caller.
      Pop  DX
     .If <c> AND                         ; Check if abnormal wakeup.
     .If <nz>                            ; And if woken by interruption.
        Pop CX                           ; Pop value from stack.
        LES BX, Dword Ptr StkRPO         ; Restore request packet addr.
        Mov DI,SI                        ; Put PSG pointer back where it belongs.
        CALLFAR UnBlockCheck             
        Mov ES:[BX+PktStatus],CCIError   ; Set Char Call Interrupted error status
        Mov DL,DevHlp_DevDone            ; Set Device Help function.
        Call DeviceHelp
        Ret
     .Endif
      Push DX                            ; Save the (no)wait indicator.
      Mov DI, SI                         ; Put PSG offset in place for
                                         ; AccessKCB.
      Mov DH, ES_DI                      ; Specify registers to get KCB addr.
                                         ; (DI already points to caller's PSG)
      CALLFAR AccessKCB                  ; ES:DI = Virt. addr. of caller's KCB.

      
      

      Mov AX, Word Ptr ES:[DI].KCBDataBfrPtr+2     ; Set seg/sel.
      Mov DI, Word Ptr ES:[DI].KCBDataBfrPtr       ; Set offset.
      Mov CX, ChDaRecLen                           ; Set length of area.
      Mov DX, 0100h + DevHlp_VerifyAccess          ; DH = r/w check,
                                                   ; DL = DevHlp function
      Call DeviceHelp               ; Verify Access to data buffer.

      Pop DX                             ; Restore the (no)wait indicator.
      Mov DI, SI                         ; Put PSG offset in place for either
                                         ; of the following paths.
     .If < c >                           ; Carry set means access denied.
        Pop CX                           ; Fix stack.
        LES BX, Dword Ptr StkRPO         ; Restore request packet address.
        Mov ES:[BX+PktStatus],ParmError  ; Indicate invalid parameter in RP
        CALLFAR UnBlockCheck             ; Start any queued thread.
        Ret                              ; Get Out Now.
     .Endif                              ; End data buffer access failed.

      ;*
      ;* Since DevHlp could have corrupted address, get KCB address
      ;* AGAIN...
      ;*

      Mov DH, ES_DI                      ; Specify registers to get KCB addr.
                                         ; (DI already points to caller's PSG)
      CALLFAR AccessKCB                  ; ES:DI = Virt. addr. of caller's KCB.

      Cli              ; Disable ints again while checking if okay to continue.
   .EndWhile           ; End Yield while KIB is empty.

    ;*
    ;* Since we may have yielded, we must check to see if Single Queue
    ;* mode was enabled while we were asleep.  If so, indicate Invalid
    ;* command and get out.
    ;*

    Test [SI].PSGFlags, SQMODE            ; If in Single Queue
   .If < nz >                             ; Mode,
      Pop   CX                            ; Fix stack.
      Mov   DI, SI                        ; DI = PSG for UnBlockCheck.
      LES   BX, Dword Ptr StkRPO          ; Get request packet address.
      CALLFAR UnBlockCheck                ; Wake up a waiting thread.
      LES   BX, Dword Ptr StkRPO          ; Restore request packet address.
      Mov   ES:[BX+PktStatus],CmdError    ; Indicate invalid parameter in RP
      Ret                                 ; Get Out Now.
   .Endif                                 ; End if in Single Queue mode.

    ;*
    ;* At this point, there is some data in the KIB.  The data is
    ;* copied from the KIB element to the caller's data buffer.
    ;* ES:DI = Caller's KCB.
    ;*
    Mov  AX,ES:[DI].KCBOutPtr          ; Get current out-pointer.
    LES  DI, ES:[DI].KCBDataBfrPtr     ; Get the saved sel:off of the
                                       ; caller's data buffer.
    Or   AX, AX                        ; Check if it's zero.
   .If <z>                             ; Is next element in end-of-chain buffer?
      Add SI,ElementZero               ; Yes, so point at it.
   .Else                               ; Else element is in element list.
      Push AX                          ; Save element index to discard.
      Mov DS, StkCBData                ; Point to KIB's segment.
      Dec AX                           ; Correct for list starting with element 1.
      IMul AX,KIBElLen                 ; Set offset into element list.
      Add AX, FLISTOFFSET              ; Point to the KIB element.
      Mov SI,AX                        ; Put in source index reg.
      Pop AX                           ; Restore KIB index of element to discard.
   .EndIf

    Mov BX, [SI].KIBRevPtr            ; Save the pointer to the next
                                      ; element in the queue.

    ;*
    ;* At this point, DS:SI points to the PSG's Element Zero, or a KIB
    ;* element, and ES:DI points to the caller's data buffer.  Copy the
    ;* CharData record from the buffer to the caller's data area.
    ;*

    Mov  CX,ChDaRecLen                ; Set number of bytes to transfer.
    ClD                               ; Make sure we don't decrement.
    Rep  MovSB                        ; Move this rec.
    Mov  DS, StkDS                    ; Point back to the DD's data
                                      ; segment.
    Mov SI, StkPSG                    ; Restore the PSG offset.
    Test DL,XPeek                     ; Check if this is a peek.
   .If <z>  NEAR                      ; Is it?
      Push DX                         ; not a peek, save (no)wait flag.
      Push BX                         ; Save new KIB head element.
      Push AX                         ; Save current KIB head element.
      Push DI                         ; Save the new offset of the
                                      ; caller's data buffer.
      Mov AX,ES:[DI-ChDaRecLen]       ; Fetch Scan/Ascii pair just read.
      Mov DH, ES_DI                   ; Specify receiving registers
      Mov DI, SI                      ;  and setup PSG offset so
      CALLFAR AccessKCB               ;  that ES:DI = caller's KCB.
      Pop Word Ptr ES:[DI].KCBDataBfrPtr;Put new data buffer offset
                                      ;  in the KCB.
      Pop AX                          ; Restore current KIB head.
      Pop BX                          ; Restore new KIB head.
      Pop DX                          ; and restore (no)wait indicator.
      Mov ES:[DI].KCBOutPtr, BX       ; Identify new KIB head in
                                      ; the caller's KCB.
     .If <BX eq -1>                   ; Is the KIB now empty?
        Mov ES:[DI].KCBInPtr, BX      ; Yes, indicate that the new
                                      ; head is also the new tail.
     .ElseIf <BX eq 0>                ; Else, is the new KIB head
                                      ;  element zero?
        Mov [SI].E0FwdPtr,-1          ; Yes, so mark it end of list.
     .Else                            ; Else new front element is in element list.
        Push AX                       ; Save current KIB head.
        Mov AX, BX                    ; Put head index into accum.
        Dec AX                        ; Correct for list starting with element 1.
        IMul AX,KIBElLen              ; Set offset into element list.
        Add AX, FLISTOFFSET           ; Point to the KIB element.
        Mov BX, AX                    ; Put back into base reg.
        Mov ES, StkCBData             ; Point to the KIB segment.
        Mov ES:[BX+KIBFwdPtr],-1      ; Mark element as end of list.
        Pop AX                        ; Restore current KIB head.
     .Endif
      Or AX, AX                       ; Check if freed element is
                                      ;  element zero.
     .If <nz>                         ; Is it?

        ;*
        ;* Freed element is not element "zero", so set up areas
        ;* appropriately and call DiscardElement to get rid of
        ;* the just-read KIB element.
        ;*

        Push DX                         ; Save (no)wait indicator.

        Push AX                         ; Save element index to be
                                        ; discarded.
        Dec  AX                         ; Correct for list starting
                                        ; with element 1.
        IMul AX, KIBElLen               ; Set offset into KIB.
        Add  AX, FLISTOFFSET            ; Point to the KIB element.
        Mov  DI, AX                     ; Put into an index register.
        Mov  ES, StkCBData              ; ES:DI-> Doomed KIB element.
        Pop  AX                         ; Restore doomed element
                                        ;  index into index register.
        CALLFAR DiscardElement          ; Discard the freed element.
        Pop  DX                         ; Restore (no)wait indicator.
        Mov  DS, StkDS                  ; Ensure DS = DD's data seg.
     .Endif                             ; End if freed element was E0.
      Lea    BX,[SI].MonBlockID         ; Get ID monitors
                                        ; may be blocked
      Mov   AX,DS                       ; 
      Push  DX                          
      Mov   DL,DevHlp_ProcRun           ; Set ProcRun DevHlp function.
      Call  [DeviceHelp]                ; Go run any blocked monitor threads
      Pop   DX                          ; Restore Wait/NoWait parm.
   .Endif                               ; End if NOT a peek.
    Mov  DI,SI                          ; Put PSG pointer back where it belongs.
    Mov  DH, ES_DI                      ; Specify receiving registers.
    CALLFAR AccessKCB                   ; ES:DI = caller's KCB.
    Pop  CX                             ; Restore transfer counter.
    Dec  CX                             ; Decrement transfer counter.
 .EndWhile                              ; End of loop of records transfered.
  Sti                                   ; Allow ints now.
  Mov DH, ES:[DI].KCBInpMode            ; Get Binary(raw)/
                                        ; ASCII(cooked) flag.
  And DH,80h                            ; Clear any bits besides mode bit.
  LES BX, Dword Ptr StkRPO              ; Restore request packet addr.
                                        
  Push CX                               ; Save count of remaining records.
  Xor  SI,SI                            ; Zero out data buffer length parm
                                        ; for IOCtlAddrCheck.
  Mov  CX,2                             ; Set parm list length parm.
  Call IOCtlAddrCheck                   ; Go verify access to parmlist.
  Pop  CX                               ; Restore count of remaining records.
 .If < nc >                             ; Carry clear means access allowed,
    Push ES                             ; Save request block seg. again.
    LES  SI,ES:[BX+ParmListPtr]         ; Get address of parm list.
    Sub  ES:[SI],CX                     ; Subtract remaining count from requested.
    Or   Byte Ptr ES:[SI+1],DH          ; Put in high bit of transfer count.
    Pop  ES                             ; Restore Req Block segment.
 .Endif                                 ; End if access allowed (PTM 3829)
  Mov DI, StkPSG                        ; Restore PSG offset.
  CALLFAR UnBlockCheck                  ; Go check if need to unblock.

  Ret

XReadOrPeek Endp

BREAK <IOCTL 76h - Get Session Manager Hot Key>
Public GetSMHotKey
GetSMHotKey Proc Far
;/***************************************************************************
;*
;* FUNCTION NAME = GetSMHotKey
;*
;* DESCRIPTION   =  Obtain session manager hot key information.
;*
;*                  This routine first ensures that the IOCTL caller's
;*                  data areas can be written to.  If so, then the parm
;*                  list word is interrogated.  If this word is zero,
;*                  the word is overwritten with a count of the MAXIMUM
;*                  number of hot keys which can be set.  If the caller
;*                  specifies a one in his parm word, then the parm list
;*                  word is overwritten with the ACTUAL number of hot
;*                  keys in use, and the DD's hot key data area is copie
;*                  to the caller's data buffer (after ensuring the data
;*                  buffer can accomodate the data).
;*
;* INPUT         =
;*                  REGISTERS:
;*
;*                  ES:BX = Ptr to Request Block
;*
;*                  The request packet contains the pointers to the various
;*                  data areas to be used.
;*
;*
;* OUTPUT        =  SI, AX, CX are not preserved.
;*                  The IOCTL caller's data area is updated.
;*
;* RETURN-NORMAL = Return to main Strategy processing.  The IOCTL
;*                 caller's data area is modified.
;*
;* RETURN-ERROR  = Same.  IOCtlAddrCheck may set an error in the request
;*                 packet.  Invalid parm returned if caller's parm list
;*                 word is neither 0 nor 1.
;**************************************************************************/
                                        
  Mov  CX,2                             ; Set parm list length.
  Xor  SI,SI                            ; No data buffer check YET.
  Call IOCtlAddrCheck                   ; Verify Access to parm list.
 .If < c >                              ; Carry set = access denied.
    Ret                                 ; Get Out Now.
 .Endif                                 ; End if access denied.
  LES  DI, ES:[BX+ParmListPtr]          ; Get address of parm list.
  Mov  AX, ES:[DI]                      ; Get first word in parm list.

 .If < AX e 0 >                         ; If caller just wants the
    Mov AL, MaxHotKeyCt                 ;  maximum hot keys used,
    Mov ES:[DI], AX                     ;  return that value.
 .ElseIf < AX e 1 >                     ; Else if the caller wants
                                        ;  the number of hot keys
                                        ;  BEING used PLUS the data,
    Mov  ES, StkRPS                     ; Restore request packet sel.
    Mov  AL, HotKeyCount                ; Get number of hot keys
    Push AX                             ;  currently set and
                                        ;  save for a moment.
    IMul AX, HKEntrySize                ; Calculate necessary size of
                                        ;  caller's data buffer.
    Mov  SI, AX                         ; Set data buffer parm for
                                        ;  IOCtlAddrCheck.
    Xor  CX,CX                          ; No parm list check this time.
    Call IOCtlAddrCheck                 ; Verify access to data buffer.
    Pop  AX                             ; Restore current hot key count.
   .If < nc >                           ; If access allowed,
      Mov CX, SI                        ; Put table size in counter.
      LES DI, ES:[BX+ParmListPtr]       ; ES:DI = Caller's parm list.
      Mov ES:[DI], AX                   ; Parm List = current hot key count.
      Mov ES, StkRPS                    ; Restore request packet sel.
      LES DI, ES:[BX+DataBfrPtr]        ; ES:DI = Caller's data buffer.
      Mov SI, Offset HotKeyTable        ; DS:SI = Table of hot keys.
                                        ; (CX already set above)
      Cld                               ; Increment memory during move.
      Rep Movsb                         ; Copy hot key table to
                                        ;  caller's data buffer.
   .Endif                               ; End if access allowed.
 .Else                                  ; Else caller specified an
                                        ;  invalid parm.
    LES BX, Dword Ptr StkRPO            ; Restore request packet address.
    Mov ES:[BX+PktStatus],ParmError     ; Indicate invalid parameter in RP
                                        ;  request packet.
 .Endif                                 ; End caller parm check.

  LES BX, Dword Ptr StkRPO              ; Restore request packet addr.
  Mov DI, StkPSG                        ; Restore PSG offset.
  Ret

GetSMHotKey Endp

BREAK <IOCTL 77h - Get Keyboard Type>
Public GetKeyboardType
GetKeyboardType Proc Far

;/***************************************************************************
;*
;* FUNCTION NAME =  GetKeyboardType
;*
;* DESCRIPTION   =  Obtain a keyboard Type from the device driver's
;*                  data area and return it to the IOCTL caller.
;*
;*                  This routine first ensures that the IOCTL caller's
;*                  data area can be written to.  If so, then the
;*                  keyboard type word is retrieved from the device
;*                  driver's data area and placed into the IOCTL caller's
;*                  data buffer, along with two reserved words of zero.
;*
;*                  Valid keyboard return types are:
;*                  00h      AT type keyboard
;*                  01h      Enhanced type keyboard
;*
;* INPUT         = REGISTERS:
;*
;*                   ES:BX = Ptr to Request Block
;*
;*                 The device driver's data area contains the keyboard type to
;*                 be copied into the caller's Data Buffer.
;*
;*
;* OUTPUT        = SI, AX are not preserved.
;*                 The keyboard type is returned to the IOCTL caller
;*
;* RETURN-NORMAL = Return to main Strategy processing.  The IOCTL
;*                 caller's data area is modified.
;*
;* RETURN-ERROR  = Same.  IOCtlAddrCheck may set an error in the request
;*                 packet.
;*
;**************************************************************************/

  Xor  CX,CX                             ; No Parameter Packet
  Mov  SI, 6                             ; Data Packet is six bytes long.
  Call IOCtlAddrCheck                    ; Verify caller's data packet
 .If < nc >                              ; If valid address
    Push ES                              ; Save request block seg.
    LES  SI,ES:[BX+DataBfrPtr]           ; Get address of data buffer.
    Mov  AX,KeyboardType                 ; Get type of this keyboard.
    Mov  ES:[SI],AX                      ; Pass to caller.
    Mov  Word Ptr ES:[SI+2], 0           ; Zero out reserved fields for compat-
    Mov  Word Ptr ES:[SI+4], 0           ; ability with Japan.
    Pop  ES                              ; Restore Request block seg.
 .Endif
  Ret

GetKeyboardType Endp

BREAK <IOCTL 78h - Get In Use Code Page>
Public GetInUseCP
GetInUseCP Proc Far
;/***************************************************************************
;*
;* FUNCTION NAME = GetInUseCP
;*
;* DESCRIPTION   = Return the value of the code page being used
;*                 by the current session, obtained from the device
;*                 driver's CPCB via an index in the KCB area of the
;*                 PSG.
;*
;*                 This routine first ensures that the IOCTL caller's
;*                 data area can be written to.  If so, then the code
;*                 page value is extracted from the CPCB and is placed
;*                 in the IOCTL caller's parm list area (via a pointer
;*                 in the Request Packet).
;*
;*
;* INPUT         =  REGISTERS:
;*
;*                    ES:BX = Ptr to Request Block
;*                    DI    = PSG address
;*
;*                    The PSG points to a KCB area which in turn points to an
;*                    entry in the CPCB, from which the Code Page value is
;*                    obtained.
;*
;* OUTPUT        =  AX, CX, DX, SI, DI are altered.
;*                  The IOCTL caller's parm list area is updated with
;*                  one of the following:
;*
;* RETURN-NORMAL =  Return to main Strategy processing.  The IOCTL
;*                  caller's parm list area is modified.
;*
;* RETURN-ERROR  =  IOCtlAddrCheck may set an error in the request
;*                  packet.
;*
;**************************************************************************/


  Xor  SI, SI                           ;Zero data buffer parm for
                                        ; IOCtlAddrCheck.
  Mov  CX, 4                            ;Set parm list length to
                                        ; validate.
  Call IOCtlAddrCheck                   ;Verify that Strategy RB data
                                        ; area can accomodate returned
                                        ; data.
 .If < c >                              ; If access NOT
                                        ; allowed,
    Ret                                 ;Get Out Now.
 .Endif                                 ;End if access not allowed.

  ; Point to KCB for caller's session.
  Mov DH, ES_DI                         ;Indicate where to put KCB
                                        ; address.
  CALLFAR AccessKCB                     ;Get caller's KCB address.
  Test Word Ptr ES:[DI].KCBFlags, CUSTOMCP ;Is the code page custom?
 .If < nz >                             ;Yes, so indicate a custom
    Mov SI, -1                          ; code page via a -1.
 .ElseIf < ES:[DI].KCBIxOffCP e 0 >     ;Not a custom code page, but
    Xor SI, SI                          ; KCB is pointing to HW XT.
 .Else                                  ;Not a custom code page,
                                        ; and not HW default XT,
    Mov  SI, ES:[DI].KCBIxOffCP         ;Get the KCB's CP index value.
    Imul SI, CPCBENTRYSIZE              ;Calculate the correct entry.
    Add  SI, CPCBOFFSET                 ;Adjust for CPCB position.

    Mov ES,CBDataSel                    ;Get selector for KIB/CPCB area.
    Mov SI, ES:[SI].CPValue             ;Get the code page value from
                                        ; the CPCB. Note use of [SI].
 .Endif                                 ;End if custom code page.

  ;*
  ;* SI now contains the code page value obtained from the CPCB, or a
  ;* -1, which indicates a custom code page.
  ;*

  LES BX, Dword Ptr StkRPO              ;Restore request packet addr.
  LES DI, ES:[BX+ParmListPtr]           ;Get the parm list area.
  Mov Word Ptr ES:[DI], SI              ;Place code page value into
                                        ; caller's data area.
  Mov Word Ptr ES:[DI+2], 00h           ;Zero out reserved word in
                                        ; IOCTL caller's Parm List
                                        ; Area.
  LES BX, Dword Ptr StkRPO              ;Restore request packet addr.
  Ret

GetInUseCP Endp

BREAK <IOCTL 79h - Translate Scan Code>
Public XlateSC
XlateSC Proc Far
;/***************************************************************************
;*
;* FUNCTION NAME =  XlateSC
;*
;* DESCRIPTION   =  Translate the scan code in the caller's Xlate
;*                  Data Record (XDR) according to the code page value
;*                  also passed by the caller of the IOCTL.
;*
;*                  This routine first ensures that the IOCTL caller's
;*                  data area can be written to.  If so, then the code
;*                  page value is determined (via a ptr. in the Request
;*                  Packet parm list).  The scan code in the caller's
;*                  XDR (obtained via the Request Packet data buffer)
;*                  is translated using the desired code page.
;*
;*  NOTE:           If the desired code page to be used is ZERO, then
;*                  the translate table of the session's active KCB
;*                  will be used.
;*
;*
;* INPUT         =       DI = PSG offset.
;*                    ES:BX = Ptr to Request Block
;*
;*                 The Request Packet parm list pointer points to the desired
;*                 code page with which to perform translation, while the
;*                 Request Packet data buffer pointer points to the XDR area
;*                 to contain the results of the translation.
;*
;* OUTPUT        =  AX, CX, DH, SI are altered.
;*                  The IOCTL caller's data buffer is updated.
;*
;* RETURN-NORMAL =  Return to main Strategy processing.  The IOCTL
;*                  caller's parm list area is modified.
;*
;* RETURN-ERROR  =  Same.  IOCtlAddrCheck may set an error in the request
;*                  packet.
;*
;**************************************************************************/

  ;*
  ;* Yield the processor until this thread is able to use the translate
  ;* data area.
  ;*

  Cli                                   ;No interrupts while checking flags
  Test MiscFlags2, XDR_IN_USE           ;Is another XlateSC active?
 .While < nz >                          ;While another XlateSC is active
    Sti                                 ;Allow interrupts.
    Call YieldProc                      ;Yield the processor.
    Cli                                 ;About to check flags again.
    Test MiscFlags2, XDR_IN_USE         ;Is XDR area still in use?
 .Endwhile                              ;End while another XlateSC.
  Or MiscFlags2, XDR_IN_USE             ;Mark the area as this
                                        ; thread's.
  Sti                                   ;Interrupts back on.

  Mov CX, 4                             ;Set Parm List Length.
  Mov SI, XDRSIZE                       ;Set Data Buffer Length.
  Call IOCtlAddrCheck                   ;Ensure caller's data areas
                                        ; are accessible.
 .If < c >                              ;If access NOT
                                        ; allowed,
    And MiscFlags2, Not XDR_IN_USE      ;Allow next XlateSC to run.
    Ret                                 ;Get Out Now.
 .Endif                                 ;End if access not allowed.

  ;*
  ;* At this point, access is allowed to the caller's data areas.
  ;* Copy caller's XLate Data Record to XDR area in data segment.
  ;*

  Mov DI, Offset XDRCharData            ;Get the offset of the DD's XDR
                                        ; data area in a dest. register.
  LDS SI, ES:[BX+DataBfrPtr]            ;Point to caller's data buffer.
  Mov ES, StkDS                         ;Set the DD's segment as the
                                        ; destination.
  Mov CX, XDRSIZE                       ;Specify number of bytes to copy
  Cld                                   ;            Ensure memory is
                                        ;  incremented.
  Rep Movsb                             ;Copy caller's data buffer to
                                        ; the data segment.
  Mov DS, StkDS                         ;Restore DS, DI with DD's data
  Mov DI, StkPSG                        ; segment and PSG offset.

  ;Obtain desired code page from caller's parm list.

  Mov ES, StkRPS                        ;Restore request packet seg.
  LES BX, ES:[BX+ParmListPtr]           ;Get address of parm list.
  Mov AX, ES:[BX]                       ;Save code page in register.

 .If < AX ne 0 >                        ;If a code page was specified:

    ;* Search CPCB for match on desired code page.

    Mov  ES, CBDataSel                  ;Get CPCB's selector.
    Mov  DI, CPCBOFFSET                 ;ES:DI = the CPCB itself.
    Mov  CX, CPCount                    ;Get number of entries in CPCB.
    Imul CX, CPCBEntrySize              ;Calculate size of CPCB.
    Add  CX, DI                         ;Determine end of CPCB.
    Test CmdFlags, CP_ENABLED           ;Check for code pages.
   .If < nz >                           ;If code pages enabled,
      Add DI, CPCBEntrySize             ;Start at CPCB[1].
                                        ; (Else start at CPCB[0])
   .Endif                               ;End if code pages enabled.
   .While < DI lt CX > AND SHORT        ;Loop on each entry while
   .While < AX ne ES:[DI].CPValue >     ; the code page is not found.
      Add DI, CPCBEntrySize             ;Point to the next CPCB entry.
   .EndWhile
   .If < DI lt CX >                     ;Was desired code page found?
      Mov AX, Word Ptr ES:[DI].CPVAddr  ;Save the selector of the XT
                                        ; for the matching CPCB entry.
      Xor BX, BX                        ;Assumed zero offset.
   .Else                                ;Code page value NOT found,
                                        ; indicate general failure.
      Mov DI, StkPSG                    ;Restore PSG offset.
      LES BX, Dword Ptr StkRPO          ; Restore request packet address.
      Mov ES:[BX+PktStatus],ParmError   ; Indicate invalid parameter in RP
      And MiscFlags2, Not XDR_IN_USE    ;Allow next XlateSC to run.
      Ret                               ; request packet. Get Out Now.
   .Endif                               ;End if code page found check.

 .Else                                  ;Caller specified NO code page.

    ;* Use translate table of active KCB for the caller's session.

    Mov  DH, ES_DI                      ;Specify receiving registers.
    CALLFAR AccessKCB                   ;ES:DI = caller's KCB.
    Test ES:[DI].KCBFlags, CUSTOMCP     ;Is KCB using a custom table?
   .If < z >                            ;NO, pick up sel:off from CPCB
      Mov  AX, ES:[DI].KCBIxOffCP       ;Get CPCB index.
      Imul AX, CPCBEntrySize            ;Calculate CPCB entry offset.
      Add  AX, CPCBOFFSET               ;Add CPCB offset in segment.
      Mov  ES, CBDataSel                ;Point to CPCB's selector.
      Mov  DI, AX                       ;Put entry offset into index reg
      Mov  AX, Word Ptr ES:[DI].CPVAddr ;Get selector to code page.
      Xor  BX, BX                       ;Assumed zero offset.
   .Else                                ;Table to be used IS custom, get
                                        ; sel:off from KCB.
      Mov AX, ES:[DI].KCBCustSel        ;AX = Custom code page selector.
      Xor BX, BX                        
     ;Mov BX, ES:[DI].KCBIxOffCP        ;BX = Custom code page offset.
   .Endif
 .Endif

  ;*
  ;* At this point AX:BX contains the sel:off of the translate table to
  ;* be used for the translation.
  ;*

  Mov DI, StkPSG                        ;Restore caller's PSG offset.

  ;*
  ;* Set up to use KbdXlate.  Includes state information.  DI will point
  ;* to the State flags to be used by KbdXlate.
  ;*

  Push AX                               ;Save translate table selector.
  Push BX                               ;Save translate table offset.

  Mov  DH, ES_DI                        ;Specify receiving registers.
  CALLFAR AccessKCB                     ;Get the caller's KCB.
  Mov  CL, ES:[DI].KCBInpMode           ;Get input mode state from
                                        ; caller's KCB to use for xlate.
  Pop  BX                               ;Restore translate table offset.
  Pop  AX                               ;Restore translate table sel.
  Mov  ES, AX                           ;Set up regs for KbdXlate.
                                        ;ES:BX -> translate table.
  Mov  SI, Offset XDRPacket             ;DS:SI -> CharData portion of
                                        ; the caller's Xlate Data rec.,
                                        ; prefixed with a dummy MonFlags
                                        ; word.
  Mov  [SI].DDFlags, 0                  ; Ensure DDFlags is 0.
                                        
  Mov  DI,offset KeyPacket2             ; Move offset of KeyPacket2 into
  Mov  StkTmpL,DI                       ; local stack var
                                        
  Mov  DI, Offset XDRFlagArea           ;DS:DI -> Flag data area to be
                                        ; used by KbdXlate.
  Mov  [DI].XInputMode, CL              ;Put input mode state in flag
                                        ; area for use in translation.
  Mov  AL, [SI].Key.Scan                ;Get passed scan code to
                                        ; translate.

 .If < AL ge 085h > AND                 ;If scan is extended F11 or F12
 .If < AL le 08Ch >                     ; 
    xor  ah, ah                         ; 
    push si                             ; 
    push ds                             ; 
    mov  DS, StkDS                      ;Get KBD Data segment
    mov  si, offset ExTable             ;Get offset of extended table scans
    sub  al, 085h                       ;Normalize the offset
    add  si, ax                         ;Add offset to start of table
    mov  AL, byte ptr ds:[si]           ;Get original scan code value
    pop  ds                             ; 
    pop  si                             ; 
 .Endif

 .If < AL ge 0C0h > AND                 ;If scan is extended extended F13
 .If < AL le 0FDh >                     ;      through ex ex F24
    xor  ah, ah                         ; 
    push si                             ; 
    push ds                             ; 
    mov  DS, StkDS                      ;Get KBD Data segment
    mov  si, offset ExExTable           ;Get offset of extended ex table scans
    sub  al, 0C0h                       ;Normalize the offset
    add  si, ax                         ;Add offset to start of table
    mov  AL, byte ptr ds:[si]           ;Get original scan code value
    pop  ds                             ; 
    pop  si                             ; 
 .Endif

  CALLFAR KbdXlate                      ;Go translate the scan code.

  Mov  AX, [SI].DDFlags                 ;Get the DDFlags word.

 .If < AX ne BadKeyCombo >              
                                        ;If the translation
                                        ; did not result in an invalid
                                        ; dead key/character
                                        ; combination, continue.

    ;*
    ;* The only case where translation would not be complete would be
    ;* when the scan code is translated into a dead key.  Check this case
    ;* and set the caller's data area appropriately.
    ;*
                                        
    And AX, KeyTypeMask                 ; Look at the returned keytype.
   .If < AX e AccentKey >               ; If scancode was found to
                                        ; be a dead key,
                                        
      Mov [DI].KBXTFlLen, 0             ; Translation is not complete.

   .Else                                ; NOT a dead key, so
      Mov [DI].KBXTFlLen, XCOMPLETE     ; Mark translation as complete.
      Mov [DI].XlateFlags, 0            ; Clear out four bytes
      Mov [DI].XKeyFlags, 0             ; worth of
      Mov [DI].XSpecFlags, 0            ; state flags.
      Mov [DI].XInputMode, 0
   .Endif

    ;Copy data segment XLate Data Record back to caller's data buffer.

    Mov SI, Offset XDRCharData          ; Point to Xlate Data Record.
    LES BX, Dword Ptr StkRPO            ; Restore request packet address.
    LES DI, ES:[BX+DataBfrPtr]          ; Make destination the caller's
                                        ; data buffer.
    Mov CX, XDRSIZE                     ; Specify byte count to copy.
    Cld                                 
                                        ; Ensure memory isincremented.
    Rep Movsb                           ; Copy Xlate Data to caller's
                                        ; data buffer.
    LES BX, Dword Ptr StkRPO            ; Restore request packet address.

 .Else                                  ; Invalid dead key/character
    LES BX, Dword Ptr StkRPO            ; Restore request packet address.
    Mov ES:[BX+PktStatus],ParmError     ; Indicate invalid parameter in RP
 .Endif                                 ; End invalid translation check.

  Mov DI, StkPSG                        ; Restore caller's PSG offset.

  ;* Mark XDR area in data segment as not being in use.

  And MiscFlags2, Not XDR_IN_USE        ; No longer needed by this thread

  Ret
XlateSC Endp

BREAK <IOCTL 7Ah - Get Keyboard Hardware ID>
Public GetKbdHWID
GetKbdHWID Proc Far
;/***************************************************************************
;*
;* FUNCTION NAME = GetKbdHWID      (7AH)      Cat. 4
;*
;* DESCRIPTION   = Get the Hardware ID of the attached keyboard
;*
;*                 The IOCTL returns the Hardware ID bytes of the
;*                 currently attached keyboard.  This allows the caller
;*                 to determine which keyboard is attached  and
;*                 allows the caller to execute keyboard specific
;*                 functions.
;*
;* NOTES         = The PC AT keyboard does not return any ID bytes when
;*                 queried.  A 2 byte value of 0001h will be returned for
;*                 this keyboard.
;*
;* INPUT         = ES:BX = Ptr to Request Packet
;*
;* OUTPUT        = registers are preserved.
;*
;*
;* RETURN-NORMAL = On Exit the data area is returned with the first
;*                 word indicating the maximum number of bytes that
;*                 the IOCTL could have returned followed by the ID
;*                 bytes of the keyboard that is attached if the
;*                 user specified a sufficient length.
;*
;* RETURN-ERROR  = A     Parameter error is returned if the user
;*                 specified a value less than 2 bytes for the max
;*                 number of bytes to be returned.
;*
;**************************************************************************/

                                          ; BEGIN:           
  Xor  CX,CX                              ; Set length for parm check.
  Mov  SI, 2                              ; No data buffer parm checking.
  Call IOCtlAddrCheck                     ; Verify read access to table.
 .If <nc>                                 ; Carry set = access denied.
                                          ;            END:
     Push ES
     LES SI, ES:[BX+DataBfrPtr]           ; Get the caller's sel/off from
                                          ; local storage.
     Mov DI, Word Ptr ES:[SI]             ; Get user specified length.
    .If <DI lt 2>                         ; If it is not at least 2 bytes
        Pop  ES                           ; then restore the req block seg
        LES  BX, Dword Ptr StkRPO         ; Restore request packet address.
        Mov  ES:[BX+PktStatus],ParmError  ; Indicate invalid parameter in RP
    .Else                                 ; Else length field is okay.
        Xor  CX,CX                        ; Set parameter list length.
        Mov  SI, DI                       ; Set data buffer length to check.
        Pop  ES                           ; Fix the stack, call may not ret.
        Call IOCtlAddrCheck               ; Verify access to data area.
        Push ES
        LES  SI, ES:[BX+DataBfrPtr]       ; Get the caller's sel/off from
                                          ; local storage.
       .If <nc>   AND                     ; If access was allowed, AND
       .If <DI ge 4>                      ; If the user length count was >= 4
           Mov AL, Byte Ptr  KbdHWIDs + 1 ; Put the Keyboard ID bytes that
           Mov Byte Ptr ES:[SI+2], AL     ; were saved from initialization
           Mov AL, Byte Ptr KbdHWIDs      ; into the next two bytes after the
           Mov Byte Ptr ES:[SI+3], AL     ; length in the user's data area.
       .Endif                             ; Endif okay to return ID bytes.
        Pop ES
    .Endif                                ; Endif proper length field given.
 .Endif                                   ;            Endif IOCtlAddr fail.
  Ret

GetKbdHWID Endp

BREAK <IOCTL 7Bh - Get Keyboard CodePage Info>
Public GetKbdCPInfo
GetKbdCPInfo Proc Far
;/***************************************************************************
;*
;* FUNCTION NAME = GetKbdCPInfo    (7BH)      Cat. 4
;*
;* DESCRIPTION   = Get the Keyboard Codepage and layout info.
;*
;*                 The IOCTL returns the currently active code page,
;*                 country, and subcountry codes for the caller's Kbd
;*                 environment.  The amount of data returned is based
;*                 on the caller's input buffer length.  Only complete
;*                 data items are returned including string delimiters.
;*
;* INPUT         = ES:BX = Ptr to Request Packet
;*
;* OUTPUT        = registers and flags are preserved.
;*
;*
;* RETURN-NORMAL = On Exit the data area is returned with the first
;*                 word indicating the number of bytes that the IOCtl
;*                 returned followed by the requested Codepage Info.
;*
;* RETURN-ERROR  = An INVALID PARM error is returned if the user
;*                 specified a value less than 2 bytes for the max
;*                 number of bytes to be returned.
;*
;**************************************************************************/

  Xor CX,CX                               ; Set length for parm check.
  Mov SI,2                                ; No data buffer parm checking.
  Call IOCtlAddrCheck                     ; Verify read access to table.
 .If <nc>  NEAR                           ; Carry set = access denied.
     Push ES
     LES  SI, ES:[BX+DataBfrPtr]          ; Get the caller's sel/off from
                                          ; local storage.
     Mov  DX, Word Ptr ES:[SI]            ; Get user specified length.
    .If <DX lt 2>                         ; If it is not at least 2 bytes
        Pop ES                            ; then restore the req block seg
        LES BX, Dword Ptr StkRPO          ; Restore request packet address.
        Mov ES:[BX+PktStatus],ParmError   ; Indicate invalid parameter in RP
    .Else NEAR                            ; Else length field is okay.
        Xor  CX,CX                        ; Set parameter list length.
        Mov  SI,DX                        ; Set data buffer length to check.
        Pop  ES                           ; Fix the stack, call may not ret.
        Call IOCtlAddrCheck               ; Verify access to data area.
       .If <nc> NEAR                      ; If access was allowed,

          ;*
          ;* Now determine the correct CPCB offset for this KCB
          ;* Get the selector for the active XT.
          ;*

           Push ES                        ; Save segment.
           Push ds                        ; save base DD data seg.
           Mov DH, DS_SI                  ; Setup receiveing registers.
           CALLFAR AccessKCB              ; Get access to current KCB.


      


       .If < DS:[SI].KCBFlags ne CUSTOMCP>    ;if not custom cp

         Mov SI, DS:[SI].KCBIxOffCP       ; Get current CP off. for this KCB.
           Imul SI, CPCBEntrySize         ; Calculate the offset into the
           Add  SI, CPCBOFFSET            ; correct CPCB entry so that we can
                                          ; 
           Pop  DS                        ; Get KBD DD data sel
           Mov  AX, CBDataSel             ; Get the Control Block Data sel
           Push DS                        ; Save KBDDD data sel (keep stack OK)
                                          ; 
           Mov  DS, AX                    ; Use the CP value and XT sel.
           Mov  AX, Word Ptr DS:[SI].CPVAddr     ; Get the XT selector.
         .Else                                   ; 
           Mov  AX, Word Ptr DS:[SI].KCBCustSel  ; get custom xt selector
         .Endif

           Pop  ds                        
           Pop  ES                        ; Restore Req Pct. Seg.
                                          ; restore base data seg.

           LES  DI, ES:[BX+DataBfrPtr]    ; Get the caller's sel/off from
                                          ; local storage.
          .If <DX ge 4> AND               ; If the requested length is large
                                          ; enough to return the Code Page...
                                          
   ; Mov AX, Word Ptr DS:[SI].CPVAddr     ; Get the XT selector.
               Mov DS, AX                 ; Put it in a segment reg.
               Mov AX, word ptr ds:CPOFF  ; Get the CP value from the XT
               Mov Word Ptr ES:[DI+2], AX ; Put into the user's data seg.
               Mov Word Ptr ES:[DI], 4    ; Put 4 in the length data field.
          .If <DX ge 7>  AND              ; If the requested length is large
                                          ; enough to return the Country...
               Mov SI, CntyOff            ; Country's offset in XT header.(16)
               Push DI                    ; Save for addressing data seg.
               Add DI, CntyDOff           ; Data area offset for Cntry. (4)
               Mov AL, Byte Ptr DS:[SI+1] ; Move the country code byte by byte
               Mov Byte Ptr ES:[DI], AL   ; into the user's data area because
               Mov AL, Byte Ptr DS:[SI]   ; it is not in string format in the
               Mov Byte Ptr ES:[DI+1], AL ; translation table.
               Pop DI                     ; Restore to address data seg.
               Mov Byte Ptr ES:[DI+6], 0  ; terminate the ASCIIZ string.
               Mov Word Ptr ES:[DI], 7    ; Put 7 in the length data field.
          .If <DX ge 12>                  ; If the requested length is large
                                          ; enough to return the SubCntry...
               Mov SI, SCntyOff           ; SCntry's offset in XT header.(20)
               Push DI                    ; Save for addressing data seg.
               Add DI, SCntyDOff          ; Data area offset for SCntry. (7)
               Mov CX, SCntyLgth          ; Length of string to copy. (4)
               Cli                        ; Disable interrupts for a bit.
               Cld                        ; Ensure direction flag.
               Rep Movsb                  ; Copy the string.
               Pop DI                     ; Restore to address data seg.
               Sti                        ; Enable interrupts again.
               Mov Byte Ptr ES:[DI+11], 0 ; terminate the ASCIIZ string.
               Mov Word Ptr ES:[DI], 12   ; Put 12 in the length data field.
          .Endif                          ; Endif requested length is OK.
           LDS DI, DWord Ptr StkPSG       ; Restore caller's PSG address.
           LES BX, DWord Ptr StkRPO       ; Restore request packet address.
       .Endif                             ; Endif okay to return CP Info.
    .Endif                                ; Endif proper length field given.
 .Endif                                   ; Endif IOCtlAddr fail.
  Ret

GetKbdCPInfo Endp

StratCode Ends
     End
