/*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.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = vkbdint.c
 *
 * DESCRIPTIVE NAME = Virtual Keyboard Device Driver (VKBD)
 *
 *
 * VERSION = V2.0
 *
 * DATE
 *
 * DESCRIPTION  VKBD Interrupt management
 *
 *          This module contains VKBD's interrupt management
 *          support:
 *
 *          1. Notification from the Keyboard PDD of physical
 *             hardware interrupt events (VKScanProc), for
 *             buffering in the VDD's virtual keyboard buffer.
 *
 *          2. Simulation of virtual interrupts via Int 09h
 *             (from VKScanProc if no simulation is in progress
 *             progress, else from the EOI or IRET hooks,
 *             depending on whether the interrupt is for an
 *             acknowledgement or scan code)
 *
 *          3. Management of EOI and IRET events during VDM's
 *             virtual interrupt pre-processing (VKEOIProc and
 *             VKIRETProc)
 *
 *          4. Final virtual interrupt processing (VKInt09Proc),
 *             including translation, plus all the operations that
 *             the ROM would normally do (other than translation)
 *
 *          5. Pre-processing of certain Int 16h keyboard services
 *             (VKInt16Proc)
 *
 *
 *      RATIONALE
 *          1. Why does the VDD buffer events, rather than relying
 *             on the PDD to buffer them, and then extracting them as
 *             requested by the VDM?
 *
 *             - The PDD's buffer is currently designed as an
 *               application-event buffer.  The VDD requires a true
 *               keyboard-event buffer, which buffers not only actual
 *               keystrokes but also acknowledgements to commands
 *               issued to the keyboard by the VDM.
 *
 *             - The VDD requires such a buffer even when the VDM is
 *               background.  For instance, a VDM might issue a series
 *               of LED-update commands to the keyboard, which requires
 *               returning a series of bytes back to the VDM.  The LEDs
 *               cannot be physically updated while the VDM is in the
 *               background, therefore the VDD must simulate the commands.
 *
 *             Although the latter could be done in cooperation with the
 *             PDD, the PDD would either (A) have to be aware that this
 *             is a background request and "simulate" it by queuing
 *             appropriate responses in the PDD buffer, or (B) provide
 *             services for adding and flushing VDD-generated events to
 *             and from its buffer.  (A) breaks our architecture by
 *             involving the PDD in "virtualization", and (B) is more
 *             complex, in that it requires more inter-component
 *             interfaces.  By maintaining BOTH our own event buffer and
 *             translation states, PDD overhead for VDMs becomes minimal;
 *             it no longer needs much, if any, per-screen group data for
 *             VDMs.
 *
 *             One key point to remember is that the Keyboard VDD is
 *             actually virtualizing TWO pieces of hardware:  the physical
 *             keyboard, which has its own circuitry and 16-event
 *             "hardware" buffer, and the keyboard controller (the 8042).
 *             Attempting to use the PDD to virtualize that "hardware"
 *             buffer is not only awkward, but is also in some sense
 *             involving the PDD in "virtualization" of the hardware, which
 *             is contrary to our design philosophy.
 *
 *          2. How are we handling the fact that, during screen group
 *             creation, the PDD will be told that a VDM is being created
 *             before it is ACTUALLY created (to provide good type-ahead
 *             support)?
 *
 *             The solution lies in having the PDD always buffer VDM
 *             keystrokes in a buffer associated with screen group #2 (the
 *             old 3xbox screen group).  This should be convenient for
 *             the PDD, since it will likely continue allocating full per-
 *             screen group data for all screen groups 0-N (where N is
 *             the max number of supported sessions other than VDMs).  This
 *             buffering will stop as soon as the VDD sends the PDD a
 *             PKBDCMD_VDMCREATED notification;  at that point, the PDD will
 *             notify the VDD of all buffered events (one call per event,
 *             just as if they generated at interrupt-time), and then stop
 *             the special buffering.
 *
 *
 *      OUTSTANDING ISSUES
 *          1. Do all keyboards with a separate Pause ignore
 *             Ctrl-Numlock?  (This was used for pausing on the older
 *             keyboards).  On a Compaq 386/20 w/101-key keyboard, It was
 *             noticed that Ctrl-Numlock had no effect.
 *
 * FUNCTIONS
 *
 * ENTRY POINTS:
 *
 * DEPENDENCIES:
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/

#define  INCL_MI                     /* for flags register bit mask equates  */
#include <mvdm.h>
#define  INCL_VIDEODEVREQ
#include <vvd.h>
#include "vkbdp.h"

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif

/*
**    External References
*/

/*
**    Global Data
*/
extern FPFNPDD fpfnPKVDDProc;         /* address of PDD entry point          */
extern USHORT usKbdHWID;              /* keyboard hardware ID                */
extern HIRQ hirq;                     /* handle for keyboard IRQ interrupt   */
extern PFNSCRNP pfnScreenProc;        /* video "screen-enable" entry point   */
extern PCPENTRY pRXHead;
extern PCPENTRY pRXDefault;
extern HHOOK hhookHotPlug;            /* pre-allocated at init time          */
extern BOOL fMarker;                  /*                                     */

/*
**     Instance Data
*/
extern FLAGS flhkVDM;
extern FLAGS flVDM;
extern ULONG nInt16Pauses;
extern HVDD hvddVideo;
extern BOOL fVPLExclusive;            /* no video peek notification          */
extern ULONG cVKBDPeekWeight;         /* peek weight to use in report        */
extern BYTE virtCommand;              /* virtual keyboard command register   */
extern BYTE virtStatus;               /* current virtual status byte         */
extern BYTE virtKbdLEDs;              /* virtual keyboard LED states         */
extern BYTE virtBuff;                 /* current virtual Buffer byte         */
extern KXLT xltBuff;                  /* translation of current Buffer byte  */
extern KKEY xltKey1;                  /* translation key packet #1           */
extern KKEY xltKey2;                  /* translation key packet #2           */
extern KXF xltKxf;                    /* translation flags packet            */
extern HHOOK hhookInt09PostInt15;     /* hook handle for VKInt09PostInt15    */
extern HHOOK hhookInt09PostBreak;     /* hook handle for VKInt09PostBreak    */
extern HHOOK hhookInt09PostPrtSc;     /* hook handle for VKInt09PostPrtSc    */
extern HHOOK hhookInt09PostSysReq;    /* hook handle for VKInt09PostSysReq   */
extern HHOOK hhookInt09PauseReturn;   /* hook handle for VKInt09PauseReturn  */
extern HHOOK hhookInt09PostIntReturn;/* hook handle for VKInt09PostIntReturn */
extern HHOOK hhookInt16ProcReturn;    /* hook handle for VKInt16ProcReturn   */
extern HHOOK hhookPasteTimeout;       /* hook handle for VKPasteTimeout      */
extern USHORT fsKeyShift;             /* keyboard shift states               */
extern KEYPKT kpCur;                  /* last key packet removed             */
extern PKEYPKT pkpScanHead;         /* ptr to head of scan code ring buffer  */
extern PKEYPKT pkpScanTail;         /* ptr to tail of scan code ring buffer  */
#pragma  BEGIN_GLOBAL_CODE

/****************************************************************************
 *
 * FUNCTION NAME = VKPDDProc
 *
 * DESCRIPTION   = PDD request router
 *
 *      This subroutine is registered by the VDD during VDD Initialization
 *      via VDHOpenPDD and is called by the PDD to provide notification
 *      of various events.  See all the VKBDCMD_KBD* equates for a brief list
 *      of those events.
 *
 * ENTRY
 *     ulFunc - request function
 *     f16p1  - parameter 1
 *     f16p2  - parameter 2
 *
 *     See vkbdpdd.h for detail parameters of request functions.
 *
 * EXIT
 *     SUCCESS
 *         return TRUE
 *     FAILURE
 *         return FALSE - invalid request function
 *
 * CONTEXT
 *     Interrupt-time
 *     Task time
 *
 * PSEUDOCODE
 *     if ulFunc == VKBDCMD_INTEVENT       //interrupt time
 *         call VKAddScan to add scan code to buffer
 *         return TRUE;
 *     else if ulFunc == VKBDCMD_CPCHANGED    //task time
 *         invalid corresponding RXTable
 *         return TRUE;
 *     else if ulFunc == VKBDCMD_HOTPLUG      //interrupt time
 *         arm task time context hook (VKHotPlugEvent)
 *         return TRUE;
 *     else
 *         return FALSE;
 *
 ****************************************************************************/

SBOOL VDDENTRY VKPDDProc(ULONG ulFunc,F16PVOID f16p1,F16PVOID f16p2)
{
  HVDM hvdm;
  PVOID pk;
  KEYPKT kp;
  BOOL flType = ADDSCAN_INTERRUPT;
  PCPENTRY pcp;
  USHORT cReference;

  AssertTRUE((ulFunc == VKBDCMD_INTEVENT) || (ulFunc == VKBDCMD_CPCHANGED) ||
     (ulFunc == VKBDCMD_HOTPLUG));
  pk = VDHQueryLin(f16p1);

  if (ulFunc == VKBDCMD_INTEVENT)
  {
    AssertTRUE(((PKINT)pk)->kint_len == KINT_PKT_LEN);
    if (hvdm = VDHHandleFromSGID(((PKINT)pk)->kint_usSGID))
    {
      memset(SSToDS(&kp), 0, sizeof(kp));
      kp.kp_Scan = (UCHAR)(((PKINT)pk)->kint_chScan);
      if (((PKINT)pk)->kint_chScan > SCAN_HIGHESTKEY)
      {
        flType |= ADDSCAN_RESPONSE;
        kp.kp_fsKey |= FKEYPKT_WAITEOI;
      }
      VKAddScan(hvdm, SSToDS(&kp), flType);
    }
  }
  else
    if (ulFunc == VKBDCMD_CPCHANGED)
    {
      ASSERTTASKONLY;
      AssertTRUE(((PKCPC)pk)->kcpc_len == KCPC_PKT_LEN);
      if (((PKCPC)pk)->kcpc_hCodePage == 0)
      {                               /* default codepage has changed        */
        cReference = pRXDefault->cp_cReference;/* save old ref. count        */
        AssertRC(vkCreateRX(0, &pRXDefault));
        pRXDefault->cp_cReference = cReference;/* restore ref. count         */
      }
      else
      {
        for (pcp = pRXHead; pcp != NULL; pcp = pcp->cp_pcpNext)
          if ((pcp->cp_usCodePage == ((PKCPC)pk)->kcpc_usCodePage) &&
             (pcp->cp_hCodePage == ((PKCPC)pk)->kcpc_hCodePage))
            break;
        if (pcp)
        {
          pcp->cp_usCodePage = 0;     /* invalid reverse xlation table       */
          if (pcp->cp_cReference == 0)
            vkDestroyRX(pcp);
        }
      }
    }
    else
      if (ulFunc == VKBDCMD_HOTPLUG)
      {
        AssertTRUE(((PKHP)pk)->khp_len == KHP_PKT_LEN);
        VDHArmContextHook(hhookHotPlug, GLOBAL_CONTEXT_HOOK);
      }
  return  TRUE;
}                                      /* VKPDDProc                          */
#pragma  END_GLOBAL_CODE
#pragma  BEGIN_SWAP_CODE

/****************************************************************************
 *
 * FUNCTION NAME = VKPasteTimeout
 *
 * DESCRIPTION   = A delay during pasting has expired
 *
 *      This subroutine is invoked when the amount of time in msecs
 *      (specified when timer hook is armed) expires before the
 *      application issues an INT 16h to read a keystroke from the
 *      buffer. This routine is needed to allow pasting to work
 *      with DOS apps like MS Works. These apps do not use the
 *      INT 16h services to read keystrokes and would hang in the
 *      middle of paste operations. The VKBD pauses pasting when
 *      keystrokes are still in the buffer to ensure proper reverse
 *      translation. Pasting is normally resumed when an INT 16h is
 *      issued by the app. Timeouts are needed for apps that don't use
 *      INT 16h to clear the BIOS buffer.
 *
 *      This routine is registered for invocation by vkPeekScan when
 *      we are in the middle of pasting and the BIOS buffer is NOT
 *      empty.
 *
 *
 * ENTRY
 *     pRefData - pointer to the ref data allocated in VDHAllocHook
 *     pcrf     - pointer to the client register frame
 *
 * EXIT
 *     return TRUE
 *
 * CONTEXT
 *     VDM local task time
 *
 * PSEUDOCODE
 *
 ****************************************************************************/

VOID HOOKENTRY VKPasteTimeout(PVOID p,PCRF pcrf)
{
  if ((flVDM&VDM_PASTEPAUSE) && (VDMBase.rb_npKHead == VDMBase.rb_npKTail))
  {
    flVDM &= ~(VDM_PASTEPAUSE+VDM_TIMERARMED);
    VKTransferScan((ULONG)CURRENT_VDM, 0);
  }
  else
    VDHArmTimerHook(hhookPasteTimeout, 500, 0);
}                                      /* VKPasteTimeout                     */

/****************************************************************************
 *
 * FUNCTION NAME = VKHotPlugEvent
 *
 * DESCRIPTION   = A keyboard has been hotplugged
 *
 *      This subroutine is called when a VKBDCMD_HOTPLUG event
 *      is posted by the PKBD to the VKBD. The hotplug event is
 *      detected by the PKBD at interrupt time. The VKBD must check
 *      if the keyboard hardware ID has changed. If so, the VKBD
 *      must ensure that the status byte at 40:96 (rb_fbKFlag3) has
 *      bit 4 (on for enhanced kbds) set properly. The virtual 40:
 *      data areas cannot be accessed at interrupt time. There is
 *      also no guarantee that the hotplug notification will arrive
 *      when a VDM is in the foreground.
 *
 *      This routine is registered for invocation at interrupt time
 *      via the VDHArmContextHook service. If the new keyboard hardware
 *      ID has changed, we will register a routine which will be called
 *      with the hvdm for all active VDMs. We can then update the 40:
 *      areas accordingly.
 *
 *
 * ENTRY
 *     pRefData - pointer to the ref data allocated in VDHAllocHook
 *     pcrf     - pointer to client register frame
 *
 * EXIT
 *     return TRUE
 *
 * CONTEXT
 *     VDM global task time
 *
 * PSEUDOCODE
 *
 ****************************************************************************/

VOID HOOKENTRY VKHotPlugEvent(PHVDM phvdm,PCRF pcrf)
{
  KID kid;
  kid.kid_len = sizeof(KID);
  AssertRC((*fpfnPKVDDProc)(PKBDCMD_QUERYID, F16PNULL, F16PFROMSSP(&kid)));
  if (kid.kid_ID != usKbdHWID)
  {
    usKbdHWID = kid.kid_ID;
    VDHEnumerateVDMs(VKKbdChanged, (ULONG)0L);
  }
}                                      /* VKHotPlugEvent                     */

/****************************************************************************
 *
 * FUNCTION NAME = VKKbdChanged
 *
 * DESCRIPTION   = Update BIOSKFLAG3_KBX
 *
 *      This subroutine will be called once for every active
 *      VDM. This routine updates the BIOSKFLAG3_KBX field
 *      (bit 4) of rb_fbKFlag3 (40:96) based on the new keyboard
 *      type. This routine is only called on the VKBDCMD_HOTPLUG
 *      event when the usKbdHWID has changed.
 *
 * ENTRY
 *     hvdm   - handle of VDM
 *     dummy  - this ULONG field is currently unused
 *
 * EXIT
 *     return TRUE
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     if this is not an enhanced keyboard
 *        turn off enhanced bit
 *     else turn enhanced bit on
 *
 ****************************************************************************/

VOID PRIVENTRY VKKbdChanged(HVDM hvdm,ULONG dummy)
{
  if (usKbdHWID == KBDID_PCAT)
    pVDMBase(hvdm)->rb_fbKFlag3 &= ~BIOSKFLAG3_KBX;
  else
    pVDMBase(hvdm)->rb_fbKFlag3 |= BIOSKFLAG3_KBX;
}                                      /* VKKbdChanged                       */

/****************************************************************************
 *
 * FUNCTION NAME = VKEOIProc
 *
 * DESCRIPTION   = EOI notification from VPIC
 *
 *      This subroutine is registered with VPIC at VDD init-time
 *      and is called by VPIC whenever a VDM issues an EOI, signifying
 *      that simulated interrupt processing is complete (or nearly so).
 *
 * ENTRY
 *     pcrf -> client register frame
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     clear fEOIPending
 *     if no transferscan pending
 *         call VKTransferScan(VDM_CURRENT, 0)
 *
 ****************************************************************************/

VOID HOOKENTRY VKEOIProc(PCRF pcrf)
{
  flVDM &= ~VDM_EOIPENDING;
  if (!(flVDM&VDM_XFERPENDING))
    VKTransferScan((ULONG)CURRENT_VDM, 0);
}                                      /* VKEOIProc                          */

/****************************************************************************
 *
 * FUNCTION NAME = VKIRETProc
 *
 * DESCRIPTION   = IRET notification from VPIC
 *
 *      This subroutine is registered with VPIC at VDD init-time
 *      and is called by VPIC whenever a VDM has returned from a simulated
 *      keyboard interrupt.  This is generally the best time to
 *      simulate further interrupts if any events are pending.
 *
 * ENTRY
 *     pcrf -> client register frame
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     clear VDM_IRETPENDING
 *     if no transferscan pending
 *         call VKTransferScan(VDM_CURRENT, 0)
 *
 ****************************************************************************/

VOID HOOKENTRY VKIRETProc(PCRF pcrf)
{
  flVDM &= ~VDM_IRETPENDING;
  if (!(flVDM&VDM_XFERPENDING))
    VKTransferScan((ULONG)CURRENT_VDM, 0);
}                                      /* VKIRETProc                         */

/****************************************************************************
 *
 * FUNCTION NAME = VKInt09Proc
 *
 * DESCRIPTION   = Default VDM h/w interrupt processing
 *
 *      This subroutine is registered with the 8086 Manager at VDM
 *      create-time and is called by the 8086 Manager whenever a
 *      VDM attempts to enter the ROM for default interrupt processing.
 *
 *      This performs the FIRST step in default interrupt processing,
 *      which is to notify the VDM's Int 15h handler of the interrupt
 *      data.
 *
 * ENTRY
 *     pcrf -> client register frame
 *
 * EXIT
 *     return FALSE for no chaining interrupt
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     call VDHPushRegs to push all VDM regs on VDM's stack
 *     call VKDisableKbd
 *     clear bufferfull bit of virtStatus
 *     call VPIC to clear virtual interrupt request
 *     call VDHPushInt to prepare for Int 15h/AX=4F00h+scan
 *     call VDHArmReturnHook to regain control in VKInt09PostInt15
 *     return to VDM
 *
 ****************************************************************************/

BOOL HOOKENTRY VKInt09Proc(PCRF pcrf)
{

  /*
  ** Carbon Copy tries to Trace into the Int 09 handler to fool
  ** the BIOS code with remote scan codes from other machine.
  ** This is a special check to see if the trace flag is on
  ** and allow the BIOS routine to run.
  */
  if (FL(pcrf)&F_TRACE)
    return  FALSE;

  VDHPopInt();
  VDHPushRegs(VDHREG_GENERAL);
  VKDisableKbd();
  virtStatus &= ~STATUS_BUFFERFULL;
  VDHClearVIRR(CURRENT_VDM, hirq);
  FL(pcrf) |= F_CARRY;                /* set VDM carry flag                  */
  AH(pcrf) = INT15AH_KEYINTERCEPT;
  AL(pcrf) = virtBuff;
  VDHPushInt(BIOSINT_OSHOOK);
  VDHArmReturnHook(hhookInt09PostInt15, VDHARH_NORMAL_IRET);

  return  FALSE;                      /* no chaining                         */
}                                      /* VKInt09Proc                        */

/****************************************************************************
 *
 * FUNCTION NAME = VKInt09PostInt15
 *
 * DESCRIPTION   = Post-Int15 processing
 *
 *      This performs the SECOND step in default interrupt processing,
 *      which includes checking to see if Int 15h requested that
 *      the buffer data be "eaten" and passing control onto the translation
 *      logic.
 *
 * ENTRY
 *     p    -> reference data (not used)
 *     pcrf -> VDM register frame
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     if VDM's carry set from Int 15h
 *         set xltBuff scan code from virtBuff
 *         set xltBuff shift states from ROM BIOS states
 *         if use key packet 2
 *             call VKInt09Translate to process key packet 2
 *         endif
 *         call VKInt09Translate to process key packet 1
 *         if not yielding
 *             call VKInt09Complete to complete the interrupt handling
 *         if we should pause
 *             call vkInt09PauseReturn
 *     else
 *         call VKInt09Complete
 *     endif
 *
 ****************************************************************************/

VOID HOOKENTRY VKInt09PostInt15(PVOID p,PCRF pcrf)
{

  /*
  ** WARNING: Not all ROMs return from INT 15h via IRET, so make force
  ** interrupt-flag on here to insure we don't hang on the PAUSE key
  */

  VDHSetFlags(FL(pcrf)|F_INTERRUPT);
  if (FL(pcrf)&F_CARRY)
  {

    /*
    */
    memset(&xltKey1, 0, sizeof(xltKey1));

    /*
    */
    xltKey1.key_len = xltKey2.key_len = KKEY_PKT_LEN;
    xltKey1.key_chScan = AL(pcrf);
    xltKey1.key_fsState = VKGetEnhancedShift(CURRENT_VDM);
    xltKxf.kxf_fsHotKey = xltKey1.key_fsState;
    AssertRC((*fpfnPKVDDProc)(PKBDCMD_TRANSLATE, F16PFROMP(&xltBuff), F16PNULL)
       );

    if ((xltKxf.kxf_fbSpec&KXFSPEC_USE2PAC) && ((xltKey2.key_fsDDFlags
       &KEYFLAGS_TYPEMASK) != KEYFLAGS_SECPREFIX))
      VKInt09Translate(pcrf, &xltKey2);

    if (VKInt09Translate(pcrf, &xltKey1))
      VKInt09Complete(pcrf);

    if (VDMBase.rb_fbKFlag1&BIOSKFLAG1_HOLDSTATE)
      VKInt09PauseReturn(NULL, pcrf);
  }
  else                                /* VDM has eaten the key               */
    VKInt09Complete(pcrf);
}                                      /* VKInt09PostInt15                   */
#pragma  END_SWAP_CODE    
#pragma  BEGIN_GLOBAL_CODE            

/****************************************************************************
 *
 * FUNCTION NAME = VKInt09Translate
 *
 * DESCRIPTION   = Int09 translation
 *
 *      This subroutine processes the translated key according to
 *      its type, updating the LEDs if necessary.
 *
 *      Note that actions can vary considerably when accent keys are
 *      considered:
 *
 *          1. Accent key pressed:
 *             The PDD translation logic must remember which Accent key
 *             it is, and tell us that it IS an KEYFLAGS_ACCENT, which
 *             we always ignore.
 *
 *          2. Accentable key pressed after Accent key:
 *             The PDD translation logic simply returns a translation
 *             for the multi-key combination;  XLTFBIT_ACCENTED is set,
 *             but we don't really care.
 *
 *          3. Un-Accentable key pressed after Accent key:
 *             The PDD translation logic may return the PREVIOUS key
 *             key (the Accent key) as KEYFLAGS_ACCENT and XLTFBIT_ACCENTED,
 *             which we map to KEYFLAGS_NORMAL;  the translation logic
 *             should include an indication that another key is pending (ie,
 *             the un-accentable key).  It too should be retrieved before we
 *             complete processing for this particular simulated interrupt.
 *             A beep should be issued at the time the accent key is
 *             returned from the translation logic.
 *
 *             Alternatively, the translation table header may indicate
 *             that the action to be taken for un-accentable keys is to drop
 *             both keystrokes, in which case KEYFLAGS_BADACCENT is returned,
 *             for which we simply beep.
 *
 *      There are some consequences of using the PDD to do ALL translation,
 *      such as the fact that "bKAlt" in the ROM BIOS Data Area will not
 *      reflect the intermediate keystrokes being pressed when the Alt key
 *      is held in conjunction with numeric keys on the keypad, in order
 *      to form a specific ASCII code.  Special logic can be added to the
 *      Int 09h processing to fix this and any other special nuances that we
 *      discover, if there is a need.  At this point, I'm skeptical that
 *      such a need exists.
 *
 *      Also note that the translation logic in the PDD, as it exists today
 *      in 1.1/1.2, has some problems.  For instance, although it can
 *      translate the Alt-keypad scenario just described, it uses per-screen
 *      group state information to do so.  This must change, so that the
 *      XLTPKT contains *everything* needed to perform a given translation.
 *      The PDD should not maintain per-screen group data for VDMs!!!  It
 *      should simply pass keyboard interrupts to us, and provide us with a
 *      few services.
 *
 *      The PDD must be able to store all the state information it needs
 *      in the XLTPKT.  The XLTPKT is derived from the structure that KBDXLATE
 *      uses today, but since we are using a INTERNAL VDD-to-PDD interface,
 *      the XLTPKT structure can be extended as needed.  In addition, there are
 *      several NEW flags defined for XLTPKT that the PDD does not currently
 *      support, including the SYSREQ flag and the PENDING bit (see vkbdpdd.h
 *      for the equates).  This support will need to be added.  -JTP
 *
 * ENTRY
 *     pcrf -> client register frame
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *
 *     if we need to use F8 marker
 *        put F8 in char field
 *     endif
 *
 *     if Alt+Esc or Ctrl+Esc or Alt+Enter pressed  AND
 *     if DOS property has one or more of these disabled
 *        put Esc or Enter scan into the buffer
 *     endif
 *
 *     if (KEYFLAGS_MULTIMAKE or KEYFLAGS_RELEASED) and
 *         not KEYFLAGS_SHIFT and
 *         not KEYFLAGS_SYSREQ
 *       change it to KEYFLAGS_UNDEFINED
 *
 *     // we may, like the ROM, want to do the following LED update
 *     // only if there is a difference between fbKFlag and fbKFlag2
 *     // states;  it probably makes no difference however  -JTP
 *
 *     if not KEYFLAGS_ACK and not KEYFLAGS_RESEND and
 *         LED states have changed
 *       call VKUpdateLEDs
 *     endif
 *
 *     // the following ID logic is taken from the ROM, and is
 *     // included here so that keyboard ID bytes are not mistakenly
 *     // interpreted as actual keystrokes  -JTP
 *
 *     if READINGID set in fbKFlag3        // begin ROM ID logic
 *       clear READINGID in fbKFlag3
 *       if scan code matches hw.hw_id1
 *         set LASTID in fbKFlag3
 *       endif
 *     else if LASTID set in fbKFlag3
 *       clear LASTID in fbKFlag3
 *       if scan code matches hw.hw_id2
 *         set KBX in fbKFlag3
 *         if SETNUMLOCK set in fbKFlag3
 *           update fbKFlag with NUMLOCK
 *           call VKUpdateLEDs
 *         endif
 *       endif
 *     else
 *       if not KEYFLAGS_SECPREFIX
 *         clear SECPREFIX bit in fbKFlag3
 *       endif
 *       switch KEYFLAGS_*
 *         case KEYFLAGS_NORMAL:
 *         case KEYFLAGS_PRTECHO:
 *           call VKAddKey on the key packet
 *           break
 *         case KEYFLAGS_ACK:
 *           set BIOSKFLAG2_ACK
 *           break
 *         case KEYFLAGS_SECPREFIX:
 *           set BIOSKFLAG3_SECPREFIX
 *           break
 *         case KEYFLAGS_OVERRUN:
 *           call VKBeep
 *           break
 *         case KEYFLAGS_RESEND:
 *           set BIOSKFLAG2_RESEND
 *           break
 *         case KEYFLAGS_DUMP:
 *         case KEYFLAGS_BADACCENT:
 *         case KEYFLAGS_UNDEFINED:
 *           break         //ignore these key types
 *         case KEYFLAGS_SHIFT:
 *           update bios LED states
 *           if LED states has changed
 *             call VKUpdateLEDs
 *           break
 *         case KEYFLAGS_PAUSE:
 *           set BIOSKFLAG1_HOLDSTATE
 *           call ScreenProc if one has registered
 *           break
 *         case KEYFLAGS_WAKEUP:
 *           clear BIOSKFLAG1_HOLDSTATE
 *           break
 *         case KEYFLAGS_ACCENT:
 *           call VKAddKey on the key packet
 *           if KEYFLAGS_USEDACCENT
 *             call VKBeep
 *           endif
 *           break
 *         case KEYFLAGS_BREAK:
 *           call VKClearKeys to clear VDM BIOS key buffer
 *           set BIOSBREAK_SET
 *           enable keyboard
 *           do an Int 1bh callout
 *           return FALSE
 *         case KEYFLAGS_PRTSC:
 *           enable keyboard
 *           send EOI
 *           do an Int 05h callout
 *           return FALSE
 *         case KEYFLAGS_PRTFLUSH:
 *           call VDHPrintClose
 *           break
 *         case KEYFLAGS_SYSREQ:
 *           if KEYFLAGS_RELEASED
 *             clear BIOSKFLAG1_SYSSHIFT
 *             prepare an Int 15h callout on SysReq break
 *           else
 *             if BIOSKFLAG1_SYSSHIFT already set
 *               break
 *             prepare an Int 15h callout on SysReq make
 *           endif
 *           send EOI
 *           enable keyboard
 *           do an Int 15h callout
 *           return FALSE
 *         default:
 *           PANIC
 *       endswitch
 *     endif
 *     return TRUE
 *
 ****************************************************************************/

BOOL PRIVENTRY VKInt09Translate(PCRF pcrf,register PKKEY pKey)
{
  BYTE fbKey,fbOldLEDs;

  /*
  **            
  */
  register INT i;
  static BYTE extExtScan[] =
  {
    EXTSCAN_F13,EXTSCAN_F14,EXTSCAN_F15,EXTSCAN_F16,EXTSCAN_F17,EXTSCAN_F18,
    EXTSCAN_F19,EXTSCAN_F20,EXTSCAN_F21,EXTSCAN_F22,EXTSCAN_F23,EXTSCAN_F24,
    EXTSCAN_SHIFTF13,EXTSCAN_SHIFTF14,EXTSCAN_SHIFTF15,EXTSCAN_SHIFTF16,
    EXTSCAN_SHIFTF17,EXTSCAN_SHIFTF18,EXTSCAN_SHIFTF19,EXTSCAN_SHIFTF20,
    EXTSCAN_SHIFTF21,EXTSCAN_SHIFTF22,EXTSCAN_SHIFTF23,EXTSCAN_SHIFTF24,
    EXTSCAN_CTRLF13,EXTSCAN_CTRLF14,EXTSCAN_CTRLF15,EXTSCAN_CTRLF16,
    EXTSCAN_CTRLF17,EXTSCAN_CTRLF18,EXTSCAN_CTRLF19,EXTSCAN_CTRLF20,
    EXTSCAN_CTRLF21,EXTSCAN_CTRLF22,EXTSCAN_CTRLF23,EXTSCAN_CTRLF24,
    EXTSCAN_ALTF13,EXTSCAN_ALTF14,EXTSCAN_ALTF15,EXTSCAN_ALTF16,EXTSCAN_ALTF17,
    EXTSCAN_ALTF18,EXTSCAN_ALTF19,EXTSCAN_ALTF20,EXTSCAN_ALTF21,EXTSCAN_ALTF22,
    EXTSCAN_ALTF23,EXTSCAN_ALTF24,EXTSCAN_CLEAR,EXTSCAN_SHIFTCLEAR,
    EXTSCAN_CTRLCLEAR,EXTSCAN_ALTCLEAR,EXTSCAN_ERASEEOF,EXTSCAN_SHIFTEEOF,
    EXTSCAN_CTRLEEOF,EXTSCAN_ALTEEOF,EXTSCAN_PA1,EXTSCAN_SHIFTPA1,
    EXTSCAN_CTRLPA1,EXTSCAN_ALTPA1,
  }
  ;
  if (fMarker)
  {                                   /* If we are to use the f8h marker     */
    if (pKey->key_chChar == 0)
    {                                 /* If char a zero                      */
      if (pKey->key_chScan >= EXTSCAN_F24)
      {                               /* If scan value >= C0 hex             */
        for (i = 0; i < sizeof(extExtScan); i++)
        {
          if (pKey->key_chScan == extExtScan[i])
          {
            pKey->key_chChar = 0xf8;  /* mark it !!                          */
            break;
          }
        }
      }
    }
  }

  
  if (virtBuff == 0xfa)
    pKey->key_fsDDFlags = 0x01;
  

  fbKey = (BYTE)(pKey->key_fsDDFlags&KEYFLAGS_TYPEMASK);

  /*
  **           
  */

  /*
  */

  if ((flhkVDM&VDM_CTRLESC) && ((VDMBase.rb_fbKFlag & ~KEYSTATE_LEDMASK) == BIOSKFLAG_CTRL))
  {
    if (pKey->key_chOrgScan == 0x01)
    {                                 /*                                     */
      VKAddKey(CURRENT_VDM, pKey->key_chChar, pKey->key_chScan);
      flVDM |= VDM_INT15RET;
    }
  }

  if ((flhkVDM&VDM_ALTESC) && ((VDMBase.rb_fbKFlag & ~KEYSTATE_LEDMASK) == BIOSKFLAG_ALT))
  {
    if (pKey->key_chOrgScan == 0x01)
    {                                 /*                                     */
      VKAddKey(CURRENT_VDM, pKey->key_chChar, pKey->key_chScan);
      flVDM |= VDM_INT15RET;
    }
  }

  if ((flhkVDM&VDM_ALTHOME) && ((VDMBase.rb_fbKFlag & ~KEYSTATE_LEDMASK) == BIOSKFLAG_ALT))
  {
    if (pKey->key_chOrgScan == 0x47)
    {                                 /*                                     */
      VKAddKey(CURRENT_VDM, pKey->key_chChar, pKey->key_chScan);
      flVDM |= VDM_INT15RET;
    }
  }

  if ((pKey->key_fsDDFlags&KEYFLAGS_MULTIMAKE) || (pKey->key_fsDDFlags
     &KEYFLAGS_RELEASED) && (fbKey != KEYFLAGS_SHIFT) && (fbKey !=
     KEYFLAGS_SYSREQ))
    fbKey = KEYFLAGS_UNDEFINED;

  if ((fbKey != KEYFLAGS_ACK) && (fbKey != KEYFLAGS_RESEND) &&
     (((VDMBase.rb_fbKFlag&KEYSTATE_LEDMASK) >> 4) != (VDMBase.rb_fbKFlag2
     &BIOSKFLAG2_LEDS)))
    VKUpdateLEDs(CURRENT_VDM);

  if (VDMBase.rb_fbKFlag3&BIOSKFLAG3_READINGID)
  {
    VDMBase.rb_fbKFlag3 &= ~BIOSKFLAG3_READINGID;
    if (pKey->key_chScan == ((PUCHAR)&usKbdHWID)[0])
      VDMBase.rb_fbKFlag3 |= BIOSKFLAG3_LASTID;
  }
  else
    if (VDMBase.rb_fbKFlag3&BIOSKFLAG3_LASTID)
    {
      VDMBase.rb_fbKFlag3 &= ~BIOSKFLAG3_LASTID;
      if (pKey->key_chScan == ((PUCHAR)&usKbdHWID)[1])
      {
        VDMBase.rb_fbKFlag3 |= BIOSKFLAG3_KBX;
        if (VDMBase.rb_fbKFlag3&BIOSKFLAG3_SETNUMLOCK)
        {
          VDMBase.rb_fbKFlag |= BIOSKFLAG_NUMLOCK;
          VKUpdateLEDs(CURRENT_VDM);
        }
      }
    }
    else
    {
      VDMBase.rb_fbKFlag3 &= ~(BIOSKFLAG3_SECPREFIX+BIOSKFLAG3_E1PREFIX);
      switch (fbKey)
      {
        case  KEYFLAGS_NORMAL :
/******************************************************* removed by   /*81250*/
/* 81250    if ((pKey->key_fsDDFlags&KEYFLAGS_SECONDARY) &&   /* BN001 */
/* 81250      (pKey->key_chChar == SCAN_E0PREFIX)) {          /* BN001 */
/* 81250       pKey->key_chChar = 0x00;                       /* BN001 */
/* 81250    }                                                 /* BN001 */
/******************************************************* removed by   /*81250*/
          /*
          */

          if (VDMBase.rb_fbKFlag1&BIOSKFLAG1_HOLDSTATE)
          {
            VDMBase.rb_fbKFlag1 &= ~BIOSKFLAG1_HOLDSTATE;
            break;
          }
          VKModifyNormalKey(pKey);

        case  KEYFLAGS_PRTECHO :
          VKAddKey(CURRENT_VDM, pKey->key_chChar, pKey->key_chScan);
          flVDM |= VDM_INT15RET;
          break;

        case  KEYFLAGS_ACK :
          VDMBase.rb_fbKFlag2 |= BIOSKFLAG2_ACK;
          break;

        case  KEYFLAGS_SECPREFIX :
          VDMBase.rb_fbKFlag3 |= BIOSKFLAG3_SECPREFIX;
          break;

        case  KEYFLAGS_OVERRUN :
          VKBeep();
          break;

        case  KEYFLAGS_RESEND :
          VDMBase.rb_fbKFlag2 |= BIOSKFLAG2_RESEND;
          break;

        case  KEYFLAGS_DUMP :
        case  KEYFLAGS_BADACCENT :
        case  KEYFLAGS_UNDEFINED :
        case  KEYFLAGS_REBOOT :
          break;                      /* ignore these key types              */

        case  KEYFLAGS_SHIFT :
/******************************************************* removed by   /*81250*/
/* 81250    if ((pKey->key_fsDDFlags&KEYFLAGS_SECONDARY) &&   /* BN001 */
/* 81250       (pKey->key_chChar == SCAN_E0PREFIX)) {         /* BN001 */
/* 81250       pKey->key_chChar = 0x00;                       /* BN001 */
/* 81250    }                                                 /* BN001 */
/******************************************************* removed by   /*81250*/
          fbOldLEDs = (BYTE)(VDMBase.rb_fbKFlag&KEYSTATE_LEDMASK);
          VKPutEnhancedShift(pKey);
          if (fbOldLEDs != (BYTE)(VDMBase.rb_fbKFlag&KEYSTATE_LEDMASK))
            VKUpdateLEDs(CURRENT_VDM);
          break;

        case  KEYFLAGS_PAUSE :
          VDMBase.rb_fbKFlag1 |= BIOSKFLAG1_HOLDSTATE;
          if (pfnScreenProc)
            (*pfnScreenProc)();
          break;

        case  KEYFLAGS_WAKEUP :
          VDMBase.rb_fbKFlag1 &= ~BIOSKFLAG1_HOLDSTATE;
          break;

        case  KEYFLAGS_ACCENT :
          /*
          ** PTR B733596 added check to make sure Char was Not Zero!
          ** Real DOS Int 9 handler does not put the Char
          ** into the Key Buffer when the Char is zero.
          ** So we now emulate that.
          ** Mueller 3/24/92
          */
          if (pKey->key_chChar != 0)
          {                           /* If Char is NOT ZERO                 */
            /*
            ** Add it to the buffer!!
            */
            VKAddKey(CURRENT_VDM, pKey->key_chChar, pKey->key_chScan);
          }                            /* endif                              */

          flVDM |= VDM_INT15RET;

          if (pKey->key_fsDDFlags&KEYFLAGS_USEDACCENT)
            VKBeep();
          break;

        case  KEYFLAGS_BREAK :

          /*
          */

          if (VDMBase.rb_fbKFlag1&BIOSKFLAG1_HOLDSTATE)
          {
            VDMBase.rb_fbKFlag1 &= ~BIOSKFLAG1_HOLDSTATE;
            break;
          }

          VKClearKeys(CURRENT_VDM);
          if (flVDM&VDM_EXTRAKEYS && VDMBase.rb_fbKFlag&BIOSKFLAG_ALT)

                    /*
                    ** This is a whizzy feature I tossed in flush all buffered
                    ** keys if ctrl-ALT-break is pressed, and it is designed to
                    ** do so WITHOUT issuing a signal;  note that it is enabled
                    ** ONLY if extended key buffering is enabled, too.
                    */

            break;
          else
          {
            VDMBase.rb_fBreak |= BIOSBREAK_SET;
            VKEnableKbd();
            VDHPushInt(BIOSINT_KEYBREAK);
            VDHArmReturnHook(hhookInt09PostBreak, VDHARH_NORMAL_IRET);
            return  FALSE;
          }
        case  KEYFLAGS_PRTSC :

          /*
          */
          if (VDMBase.rb_fbKFlag1&BIOSKFLAG1_HOLDSTATE)
          {
            VDMBase.rb_fbKFlag1 &= ~BIOSKFLAG1_HOLDSTATE;
            break;
          }

          VKEnableKbd();
          VDHSendVEOI(hirq);
          VDHPushInt(BIOSINT_PRINTSCREEN);

          /*
          ** Using recursive iret hook to prevent possible reentrance
          ** problem.
          */
          VDHArmReturnHook(hhookInt09PostPrtSc, VDHARH_RECURSIVE_IRET);
          return  FALSE;

        case  KEYFLAGS_PRTFLUSH :
          VDHPrintClose(CURRENT_VDM);
          break;

        case  KEYFLAGS_SYSREQ :
          if (pKey->key_fsDDFlags&KEYFLAGS_RELEASED)
          {
            VDMBase.rb_fbKFlag1 &= ~BIOSKFLAG1_SYSSHIFT;
            AL(pcrf) = 0x01;          /* break of SysReq                     */
          }
          else
          {
            if (VDMBase.rb_fbKFlag1&BIOSKFLAG1_SYSSHIFT)
              break;
            VDMBase.rb_fbKFlag1 |= BIOSKFLAG1_SYSSHIFT;
            AL(pcrf) = 0x00;          /* make of SysReq                      */
          }

          VDHSendVEOI(hirq);
          VKEnableKbd();
          AH(pcrf) = INT15AH_KEYSYSREQ;
          VDHPushInt(BIOSINT_OSHOOK);
          /*
          ** Using recursive iret hook to prevent possible reentrance
          ** problem.
          */
          VDHArmReturnHook(hhookInt09PostSysReq, VDHARH_RECURSIVE_IRET);
          return  FALSE;

#ifdef   VDDSTRICT
        default  : /* These key types should never be returned in raw mode: */

          /*
          ** REBOOT,PSPAUSE,HOTKEY,READPEEK,PSBREAK,PSPRTECHO
          */
          PRINTDEBUG("VKInt09Translate: unexpected key type");
#endif
      }                               /* end-switch                          */
    }
  return  TRUE;
}                                      /* VKInt09Translate                   */
#pragma  END_GLOBAL_CODE  
#pragma  BEGIN_SWAP_CODE         

/****************************************************************************
 *
 * FUNCTION NAME = VKInt09PauseReturn
 *
 * DESCRIPTION   = Post-PAUSE processing
 *
 *      This routine is called when we have completed processing
 *      the Int 9h and the pause bit is ON in the 40: data area.
 *
 *      It is IMPORTANT that this routine does not issue the
 *      VDHWaitVIRRs when there are still scans to be processed.
 *      When they stopped simulating timer ticks to VDM (unless
 *      the app really needed them) the old version of this
 *      routine would block on the first scan that set the pause
 *      bit ON. The other scans would not get processed until
 *      further keys were hit. This caused the wakeup to sometimes
 *      take several keystrokes to respond.
 *
 *      If the pause bit is ON this routine will always get called
 *      before exiting the Int 9h handler ensuring that we do pause
 *      when needed.
 *
 *      NOTE: The VDHWaitVIRRs returns on two conditions.
 *
 *            1.) An interrupt was simulated to a VDD. Under this
 *                condition we will be re-invoked upon completion
 *                of that simulated interrupt. VDHWaitVIRRS will
 *                return TRUE although we don't check the RC.
 *
 *            2.) A VDD issued a VDHWakeVIRRs. We will not get
 *                re-invoked and VDHWaitVIRRs will return FALSE.
 *
 *
 * ENTRY
 *     p    - reference data (unused)
 *     pcrf - pointer to client register frame (unused)
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time (during Int 09h processing)
 *
 * PSEUDOCODE
 *     if there are no more scans to be processed AND
 *     if the pause bit is ON in the 40: data byte
 *       call VDHWaitVIRRs to block until virtual interrupt occurs
 *
 ****************************************************************************/

VOID HOOKENTRY VKInt09PauseReturn(PVOID p,PCRF pcrf)
{
  if ((pkpScanHead == pkpScanTail) &&
      (VDMBase.rb_fbKFlag1&BIOSKFLAG1_HOLDSTATE ))
    VDHWaitVIRRs(hhookInt09PauseReturn);
}

/****************************************************************************
 *
 * FUNCTION NAME = VKInt09PostBreak
 *
 * DESCRIPTION   = Post-BREAK processing
 *
 *      This performs the SECOND step in BREAK key processing, which is
 *      to add 0,0 to the VDM's ROM BIOS keyboard buffer.
 *
 * ENTRY
 *     p    - reference data (unused)
 *     pcrf - pointer to client register frame (unused)
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time (during Int 09h processing)
 *
 * PSEUDOCODE
 *     call VKAddKey(0,0)
 *     jump to VKInt09IntRet
 *
 ****************************************************************************/

VOID HOOKENTRY VKInt09PostBreak(PVOID p,PCRF pcrf)
{
  VKAddKey(CURRENT_VDM, 0, 0);
  VDHSendVEOI(hirq);
  flVDM |= VDM_INT15RET;
  VKInt09IntRet(pcrf);
}                                      /* VKInt09PostBreak                   */

/****************************************************************************
 *
 * FUNCTION NAME = VKInt09PostPrtSc
 *
 * DESCRIPTION   = Post-print-screen processing
 *
 *      This performs the SECOND step in PRTSC key processing, which is
 *      to simply return;  the keyboard has already been enabled and
 *      EOI has already been simulated.
 *
 * ENTRY
 *     p    - reference data (unused)
 *     pcrf - pointer to client register frame (unused)
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time (during Int 09h processing)
 *
 * PSEUDOCODE
 *     call VKInt09Return
 *
 ****************************************************************************/

VOID HOOKENTRY VKInt09PostPrtSc(PVOID p,PCRF pcrf)
{
  VKInt09Return(pcrf);
}                                      /* VKInt09PostPrtSc                   */

/****************************************************************************
 *
 * FUNCTION NAME = VKInt09PostSysReq
 *
 * DESCRIPTION   = Post-SYSREQ processing
 *
 *      This regains control after simulating an Int 15h for the SYSREQ
 *      key.  There is nothing to do but simply return, as the keyboard has
 *      already been enabled and EOI has already been simulated.  This
 *      has been defined as a separate entry point however, to facilitate
 *      debugging and for extensibility.
 *
 * ENTRY
 *     p    - reference data (unused)
 *     pcrf - pointer to client register frame (unused)
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time (during Int 09h processing)
 *
 * PSEUDOCODE
 *     call VKInt09Return
 *
 ****************************************************************************/

VOID HOOKENTRY VKInt09PostSysReq(PVOID p,PCRF pcrf)
{
  VKInt09Return(pcrf);
}                                      /* VKInt09PostSysReq                  */

/****************************************************************************
 *
 * FUNCTION NAME = VKInt09Complete
 *
 * DESCRIPTION   = Final Int09 processing
 *
 *      This performs the FINAL step in default interrupt processing,
 *      which includes EOIing the interrupt, enabling the keyboard, and
 *      returning.
 *
 * ENTRY
 *     pcrf -> client register frame
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time (during Int 09h processing)
 *
 * PSEUDOCODE
 *     call VDHSendVEOI to simulate the EOI
 *     call VKEnableKbd to simulate the keyboard enable
 *     call VKInt09IntRet
 *
 ****************************************************************************/

VOID PRIVENTRY VKInt09Complete(PCRF pcrf)
{
  VDHSendVEOI(hirq);
  VKEnableKbd();
  VKInt09IntRet(pcrf);
}                                      /* VKInt09Complete                    */

/****************************************************************************
 *
 * FUNCTION NAME = VKInt09IntRet
 *
 * DESCRIPTION   = Return from Int09 processing
 *
 *      ENTRY
 *          pcrf -> client register frame
 *
 *      EXIT
 *          None
 *
 *      CONTEXT
 *          VDM Task-time (during Int 09h processing)
 *
 *      PSEUDOCODE
 *          if VDM_INT15RET is set {
 *              prepare an Int 15h callout (AX=9102)
 *              arm return hook to get back control after callout completes;
 *          } else
 *              call VKInt09Return;
 *
 ****************************************************************************/

VOID PRIVENTRY VKInt09IntRet(PCRF pcrf)
{
  if (flVDM&VDM_INT15RET)
  {
    flVDM &= ~VDM_INT15RET;
    AX(pcrf) = (INT15AH_INTCOMPLETE << 8)+INT15AL_KEYBOARD;
    VDHPushInt(BIOSINT_OSHOOK);

    /*
    ** Using recursive iret hook to prevent possible reentrance
    ** problem.
    */
    VDHArmReturnHook(hhookInt09PostIntReturn, VDHARH_RECURSIVE_IRET);
    return ;
  }
  VKInt09Return(pcrf);
}                                      /* VKInt09IntRet                      */

/****************************************************************************
 *
 * FUNCTION NAME = VKInt09PostIntReturn
 *
 * DESCRIPTION   = Post interrupt return processing
 *
 * ENTRY
 *     p    -> reference data (not used)
 *     pcrf -> client register frame
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time (during Int 09h processing)
 *
 * PSEUDOCODE
 *     call VKInt09Return;
 *
 ****************************************************************************/

VOID HOOKENTRY VKInt09PostIntReturn(PVOID p,PCRF pcrf)
{
  VKInt09Return(pcrf);
}                                      /* VKInt09PostIntReturn               */

/****************************************************************************
 *
 * FUNCTION NAME = VKInt09Return
 *
 * DESCRIPTION   = Exit from Int09 processing
 *
 *      This routes control from Int 09 processing back to V86 mode,
 *      eating the interrupt in the process and thereby preventing the
 *      ROM from ever running.
 *
 * ENTRY
 *     None
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time (during Int 09h processing)
 *
 * PSEUDOCODE
 *     call VDHPopRegs to clean up VDM's stack
 *     call VDHPopInt to prevent execution from entering the ROM
 *     return to VDM
 *
 ****************************************************************************/

VOID PRIVENTRY VKInt09Return(PCRF pcrf)
{
  VDHPopRegs(VDHREG_GENERAL);

  /*
  ** WARNING: Not all ROMs return from s/w INTs via IRET, so force
  ** interrupt-flag to state it must have been initially (ie, enabled)
  */

  VDHSetFlags(FL(pcrf)|F_INTERRUPT);
}                                      /* VKInt09Return                      */

/****************************************************************************
 *
 * FUNCTION NAME = VKDisableKbd
 *
 * DESCRIPTION   = Virtual keyboard disable
 *
 *      This disables the virtual keyboard, which is relatively unimportant
 *      to us, but if during the course of Int 09h processing we must return
 *      to the VDM, it might expect the keyboard to be truly disabled.
 *
 * ENTRY
 *     None
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     set COMMAND_DISABLEKBD in virtCommand
 *
 ****************************************************************************/

VOID PRIVENTRY VKDisableKbd(VOID)
{
  virtCommand |= COMMAND_DISABLEKBD;
}                                      /* VKDisableKbd                       */

/****************************************************************************
 *
 * FUNCTION NAME = VKEnableKbd
 *
 * DESCRIPTION   = Virtual keyboard enable
 *
 *      This re-enables the virtual keyboard, undoing the DisableKbd call
 *      above.
 *
 * ENTRY
 *     None
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     clear COMMAND_DISABLEKBD in virtCommand
 *
 ****************************************************************************/

VOID PRIVENTRY VKEnableKbd(VOID)
{
  virtCommand &= ~COMMAND_DISABLEKBD;
}                                      /* VKEnableKbd                        */

/****************************************************************************
 *
 * FUNCTION NAME = VKUpdateLEDs
 *
 * DESCRIPTION   =
 *
 *      This updates the LEDs according to the state bits in the ROM
 *      BIOS data area.
 *
 * ENTRY
 *     hvdm - VDM handle
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     Task-time
 *
 * PSEUDOCODE
 *     clear ACK and RESEND bits in fbKFlag2
 *     transfer SCROLLOCK, NUMLOCK, and CAPSLOCK bits to virtKbdLEDs
 *     update fbKFlag2 LED states as well
 *         (with appropriate bits transposition)
 *     if VDM_FOCUS
 *       call (*pfnPKVDDProc)(PKBDCMD_SETLEDS, virtKbdLEDs)
 *     endif
 *
 ****************************************************************************/

VOID PRIVENTRY VKUpdateLEDs(HVDM hvdm)
{
  KLEDS kleds;
  pVDMBase(hvdm)->rb_fbKFlag2 &= ~(BIOSKFLAG2_ACK|BIOSKFLAG2_RESEND);
  REFHVDM(hvdm, BYTE, virtKbdLEDs) = (BYTE)((pVDMBase(hvdm)->rb_fbKFlag
     &(BIOSKFLAG_SCROLLLOCK|BIOSKFLAG_NUMLOCK|BIOSKFLAG_CAPSLOCK)) >> ZEROBITS
     (BIOSKFLAG_SCROLLLOCK));
  pVDMBase(hvdm)->rb_fbKFlag2 &= ~BIOSKFLAG2_LEDS;
  pVDMBase(hvdm)->rb_fbKFlag2 |= REFHVDM(hvdm, BYTE, virtKbdLEDs);

  /*
  ** no error check, assuming PDD call will not fail
  */
  if (REFHVDM(hvdm, BOOL, flVDM)&VDM_FOCUS)
  {
    kleds.kled_len = sizeof(KLEDS);
    kleds.kled_fsLEDs = REFHVDM(hvdm, BYTE, virtKbdLEDs);
    AssertRC((*fpfnPKVDDProc)(PKBDCMD_SETLEDS, F16PFROMSSP(&kleds), F16PNULL));
  }
}                                      /* VKUpdateLEDs                       */

/****************************************************************************
 *
 * FUNCTION NAME = VKInt16Proc
 *
 * DESCRIPTION   =
 *
 *      VDM Int 16h interception
 *
 *      This subroutine is registered with the 8086 Manager at VDM
 *      create-time and is called by the 8086 Manager whenever a
 *      VDM attempts to enter the ROM for default Int 16h processing.
 *
 * ENTRY
 *     pcrf   -> VDM register frame
 *
 * EXIT
 *     return FALSE to chain interrupts
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     if function is PEEKCHAR, QUERYSHIFT, PEEKEXTCHAR, QUERYEXTSHIFT
 *       call VDHReportPeek
 *     else if function is READCHAR, READEXTCHAR
 *       call VKInt16ProcReturn
 *     endif
 *     // nothing special about WRITECHAR and SETDELAYS I think -JTP
 *     return to VDM           // let ROM gain control now
 *
 ****************************************************************************/

BOOL HOOKENTRY VKInt16Proc(PCRF pcrf)
{
  register BYTE ah;
  INT nFreeSpace;
  ah = AH(pcrf);
  VDHSetFlags(FL(pcrf)|F_INTERRUPT);  /* enable ints for VDHReportPeek       */

  
  if (flVDM&VDM_PASTING)
  {

    /*
    ** For fast pasting, see if we need to kick the Shield again
    */

    if (flVDM&VDM_FASTPASTE)
    {

      /*
      ** If pasting has completed (or been aborted), clean up
      */

      if (flVDM&VDM_ENDPASTING || flVDM&VDM_PASTEDONE && VDMBase.rb_npKHead ==
         VDMBase.rb_npKTail)
        vkSysEndPaste(CURRENT_VDM);
      else
      {
        nFreeSpace = (INT)(VDMBase.rb_npKHead-VDMBase.rb_npKTail)/2-1;
        if (nFreeSpace < 0)
          nFreeSpace += NElements(VDMBase.rb_abKBuf);

        /*
        ** If buffer is empty or close to empty, start refill
        */

        if (nFreeSpace == KEYSBUF_THRESHOLD || nFreeSpace == NElements
           (VDMBase.rb_abKBuf)-1)
          VDHRequestVDD(hvddVideo, CURRENT_VDM, VVDDEVREQ_POSTPASTE, (PVOID)
             TRUE, NULL);
      }
    }

    /*
    ** For non-fast pasting, apply the usual pausing facilities
    */

    else
    {

      /*
      ** This controls pausing at the INT 16h level
      */
      if ((ah == INT16_PEEKCHAR) || (ah == INT16_PEEKEXTCHAR) || (ah ==
         INT16_PEEK122CHAR))
      {
        if (nInt16Pauses > 0)
        {
          nInt16Pauses--;
          VDHPopInt();                /* pretend nothing available now       */
          FL(pcrf) |= F_ZERO;
          return  TRUE;               /* don't chain either                  */
        }
      }
      else
        if ((ah == INT16_READCHAR) || (ah == INT16_READEXTCHAR) || (ah ==
           INT16_READ122CHAR))
          nInt16Pauses = 4;           

      /*
      **  This controls pausing at the hardware scan-code level,
      **  but unfortunately is dependent on the app using INT 16h;
      **  for apps that don't (like Win20) I think we need timeouts, too
      */
      if (VDMBase.rb_npKHead == VDMBase.rb_npKTail)
      {
        if (flVDM&VDM_PASTEPAUSE)
        {
          flVDM &= ~VDM_PASTEPAUSE;
          if (flVDM&VDM_TIMERARMED)
          {
            VDHDisarmTimerHook(hhookPasteTimeout);
            flVDM &= ~VDM_TIMERARMED;
          }
        }
        VKTransferScan((ULONG)CURRENT_VDM, 0);
      }
    }
  }
  
  VKTransferExtraKey();
  if ((ah == INT16_PEEKCHAR) || (ah == INT16_QUERYSHIFT) || (ah ==
     INT16_PEEKEXTCHAR) || (ah == INT16_QUERYEXTSHIFT) || (ah ==
     INT16_PEEK122CHAR) || (ah == INT16_QUERY122SHIFT))
  {
    if (!fVPLExclusive)
      VKInt16PostVDD(VVDDEVREQ_POSTPEEK);
    VDHReportPeek(cVKBDPeekWeight);   /* VPOL peek notification              */
  }
  else
    if ((ah == INT16_READCHAR) || (ah == INT16_READEXTCHAR) || (ah ==
       INT16_READ122CHAR))
    {
      VKInt16PostVDD(VVDDEVREQ_POSTREAD);
      VKInt16ProcReturn(NULL, pcrf);
    }

  return  FALSE;                      /* allow interrupt chaining            */
}                                      /* VKInt16Proc                        */

/****************************************************************************
 *
 * FUNCTION NAME = VKInt16PostVDD
 *
 * DESCRIPTION   = Int16 post VDD notification
 *
 * ENTRY
 *     ulCmd == command # to post
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *
 ****************************************************************************/

VOID PRIVENTRY VKInt16PostVDD(ULONG ulCmd)
{
  BOOL fData = (VDMBase.rb_npKHead != VDMBase.rb_npKTail);

  if (!fData)
    flVDM &= ~VDM_PASTEDATA;

  if (!(flVDM&(VDM_PASTING|VDM_PASTEDATA)))
    if (flVDM&VDM_WINDOWED && hvddVideo)
      VDHRequestVDD(hvddVideo, CURRENT_VDM, ulCmd, (PVOID)fData, NULL);
}                                      /* VKInt16PostVDD                     */

/****************************************************************************
 *
 * FUNCTION NAME = VKInt16ProcReturn
 *
 * DESCRIPTION   = Int16 post-interrupt processing
 *
 *      This regains control of the VDM after one or more interrupts
 *      were simulated while waiting for data to be made available via
 *      Int 16h.
 *
 * ENTRY
 *     p    - reference data (unused)
 *     pcrf - pointer to client register frame (unused)
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     if ROM BIOS input buffer empty
 *       call VDHWaitVIRRs to block until virtual interrupt occurs
 *     endif
 *     return to VDM           // let ROM or VPIC gain control now
 *
 ****************************************************************************/

VOID HOOKENTRY VKInt16ProcReturn(PVOID p,PCRF pcrf)
{
  if (VDMBase.rb_npKHead == VDMBase.rb_npKTail)
  {
    VDHSetFlags(FL(pcrf)|F_INTERRUPT);
    VDHWaitVIRRs(hhookInt16ProcReturn);
  }
}                                      /* VKInt16ProcReturn                  */

/****************************************************************************
 *
 * FUNCTION NAME = BIOSKFLAG1_SHIFTSTAT
 *
 * DESCRIPTION   = Get Enhanced Key Shift State
 *
 *      This routine reads the BIOS KFlags and returns the shift
 *      state of the enhanced keyboard in an enhanced shift state
 *      format.
 *
 *      The bit format returned is identical to Int 16 (AH=12h) as
 *      well as the fsShift bit format in the translate packet.
 *
 * ENTRY
 *     hvdm -> VDM
 *
 * EXIT
 *     Returns Enhanced Shift States:
 *         BIOSKFLAG_RIGHTSHIFT    0x0001
 *         BIOSKFLAG_LEFTSHIFT     0x0002
 *         BIOSKFLAG_CTRL          0x0004
 *         BIOSKFLAG_ALT           0x0008
 *         BIOSKFLAG_SCROLLLOCK    0x0010
 *         BIOSKFLAG_NUMLOCK       0x0020
 *         BIOSKFLAG_CAPSLOCK      0x0040
 *         BIOSKFLAG_INSERT        0x0080
 *         BIOSKFLAG1_LEFTCTRL     0x0100
 *         BIOSKFLAG1_LEFTALT      0x0200
 *         BIOSKFLAG3_RIGHTCTRL    0x0400
 *         BIOSKFLAG3_RIGHTALT     0x0800
 *         BIOSKFLAG1_SCROLLDOWN   0x1000
 *         BIOSKFLAG1_NUMDOWN      0x2000
 *         BIOSKFLAG1_CAPSDOWN     0x4000
 *         BIOSKFLAG1_SYSSHIFT     0x8000
 *
 * CONTEXT
 *     Task-time
 *
 * PSEUDOCODE
 *     assemble and transpose bits from fbKFlag, fbKFlag1 and fbKFlag3
 *     bytes of the BIOS data area;
 *
 ****************************************************************************/

#define BIOSKFLAG1_SHIFTSTATES  (BIOSKFLAG1_CAPSDOWN   | BIOSKFLAG1_NUMDOWN | \
                                 BIOSKFLAG1_SCROLLDOWN | BIOSKFLAG1_LEFTALT | \
                                 BIOSKFLAG1_LEFTCTRL)

/****************************************************************************
 *
 * FUNCTION NAME = VKGetEnhancedShift
 *
 * DESCRIPTION   =
 *
 *
 * INPUT         = (HVDM hvdm)
 *
 * OUTPUT        = NONE
 *
 * RETURN-NORMAL = NONE
 *
 * RETURN-ERROR  = NONE
 *
 ****************************************************************************/

USHORT PRIVENTRY VKGetEnhancedShift(HVDM hvdm)
{
    return (USHORT)
        (((REFHVDM(hvdm, BYTE, VDMBase.rb_fbKFlag1) &
           BIOSKFLAG1_SYSSHIFT) <<
          (ZEROBITS(KEYSTATE_SYSREQDOWN) - ZEROBITS(BIOSKFLAG1_SYSSHIFT))) |
         ((REFHVDM(hvdm, BYTE, VDMBase.rb_fbKFlag1) & BIOSKFLAG1_SHIFTSTATES)
          << 8) |
         ((REFHVDM(hvdm, BYTE, VDMBase.rb_fbKFlag3) &
                  (BIOSKFLAG3_RIGHTALT | BIOSKFLAG3_RIGHTCTRL)) << 8) |
         (REFHVDM(hvdm, BYTE, VDMBase.rb_fbKFlag)));
}                                      /* VKGetEnhancedShift                 */

/****************************************************************************
 *
 * FUNCTION NAME = VKPutEnhancedShift
 *
 * DESCRIPTION   = Put Enhanced Key Shift State into BIOS data area
 *
 *      This routine stores the given extended shift states into
 *      the correct bit positions of various data bytes in the BIOS
 *      data area.
 *
 *      Refer to vdmbios.h for BIOS data byte formats.
 *
 * ENTRY
 *     hvdm -> VDM
 *     usShift - extended shift states
 *
 * EXIT
 *     None
 *
 * CONTEXT
 *     VDM Task-time
 *
 * PSEUDOCODE
 *     store appropriate bits into appropriate bytes of the BIOS
 *     data area.
 *
 ****************************************************************************/

VOID PRIVENTRY VKPutEnhancedShift(register PKKEY pKey)
{
  BOOL fInsert = FALSE;
  USHORT usShift = pKey->key_fsState;

  /*
  ** Update KFLAG1
  */

  VDMBase.rb_fbKFlag = (BYTE)usShift;

  /*
  ** Update KFLAG2 by preserving current HOLDSTATE and INSDOWN bits and
  ** setting the SHIFTSTATES and SYSSHIFT bits to match given settings
  */

  VDMBase.rb_fbKFlag1 = (BYTE)((VDMBase.rb_fbKFlag1&(BIOSKFLAG1_HOLDSTATE|
     BIOSKFLAG1_INSDOWN))|(BYTEOF(usShift, 1)&BIOSKFLAG1_SHIFTSTATES)|((usShift
     &KEYSTATE_SYSREQDOWN) >> (ZEROBITS(KEYSTATE_SYSREQDOWN)-ZEROBITS
     (BIOSKFLAG1_SYSSHIFT))));

  VDMBase.rb_fbKFlag3 &= ~(BIOSKFLAG3_RIGHTCTRL|BIOSKFLAG3_RIGHTALT);
  VDMBase.rb_fbKFlag3 |= BYTEOF(usShift, 1)&(BIOSKFLAG3_RIGHTCTRL|
     BIOSKFLAG3_RIGHTALT);

/*
** We make an explicit test for the INSERT key to correctly
**   maintain the INSDOWN bit;  the PDD translation service lumps the
**   INSERT key with all other shift keys
*/

if (pKey->key_chScan == EXTSCAN_INS ||    
                                          /* SHIFT-INS (w/num-lock off) as   */
                                          /*                   a shift key   */
/*
**           instead of as a normal key (ie, the digit '0');  this logic
**           already happened to fix that case, but did NOT fix CTRL-INS
**           and ALT-INS cases, hence the special checks for EXTSCAN_CTRLINS
**           and EXTSCAN_ALTINS, respectively...
*/
     ((pKey->key_chChar == 0 || pKey->key_chChar == SCAN_E0PREFIX) &&
     (pKey->key_chScan == EXTSCAN_CTRLINS || pKey->key_chScan == EXTSCAN_ALTINS
     )))
  {

    /*
    ** Determine if this a TRUE INSERT or not (see comment above)
    */

    if (pKey->key_chScan == EXTSCAN_INS && (pKey->key_chChar == 0 ||
       pKey->key_chChar == SCAN_E0PREFIX))
      fInsert++;

    /*
    ** Generate a key only if this is the MAKE of the XXXXX-INSERT
    */

    if (!(pKey->key_fsDDFlags&KEYFLAGS_RELEASED))
    {
      if (fInsert)
        VDMBase.rb_fbKFlag1 |= BIOSKFLAG1_INSDOWN;
      VKAddKey(CURRENT_VDM, pKey->key_chChar, pKey->key_chScan);
      flVDM |= VDM_INT15RET;
    }
    else
    {
      if (fInsert)
        VDMBase.rb_fbKFlag1 &= ~BIOSKFLAG1_INSDOWN;
    }
  }

  BYTEOF(fsKeyShift, 0) = (BYTE)(VDMBase.rb_fbKFlag&(BIOSKFLAG_RIGHTSHIFT+
     BIOSKFLAG_LEFTSHIFT+BIOSKFLAG_CTRL+BIOSKFLAG_ALT));
  BYTEOF(fsKeyShift, 1) = (BYTE)(VDMBase.rb_fbKFlag1&(BIOSKFLAG1_LEFTCTRL+
     BIOSKFLAG1_LEFTALT)|VDMBase.rb_fbKFlag3&(BIOSKFLAG3_RIGHTCTRL+
     BIOSKFLAG3_RIGHTALT));
}                                      /* VKPutEnhancedShift                 */

/****************************************************************************
 *
 * FUNCTION NAME = VKModifyNormalKey
 *
 * DESCRIPTION   = Adjust key packet if it contains an "extended" key
 *
 *      This checks for an "extended key" (ie, one that an extended BIOS
 *      would ignore on a normal INT 16h (AH=00h) read but not on an extended
 *      INT 16h (AH=10h) read), and converts it to the format expected by
 *      the extended BIOS prior to stuffing it into the ROM BIOS buffer.
 *
 *      Extended keys are key combinations that only exist on an extended
 *      (ie, 101-key) keyboard (eg, F11) OR that pre-extended BIOSes never
 *      supported (eg, Alt-Backspace).  IBM assigned most of these new key
 *      combinations values above 84h (Ctrl-PgUp), and normal INT 16h (AH=00h)
 *      reads ignore extended keys above 84h, but IBM also assigned new
 *      keys some previously unused values BELOW 84h (which IBM's ROM BIOS
 *      listing refers to as "fill-ins").  In order to enable INT 16h code to
 *      filter out the "fill-ins" as well, they flag them with an F0h instead
 *      of the usual 00h in the character byte, and then either throw away
 *      such a key on a normal INT 16h read, or return it with 00h in the
 *      character byte on an extended INT 16h read.  And of course, they always
 *      have to first check a F0h-flagged key for 00h in the scan byte, in
 *      case the user generated an F0h character by typing ALT-2-4-0.
 *
 *      Fortunately, all we have to worry about is flagging the key correctly,
 *      since we only replace INT 09h, not INT 16h.
 *
 * ENTRY
 *     pKey -> key packet
 *
 * EXIT
 *     Key packet possibly modified
 *
 * CONTEXT
 *     VDM Task-time
 *
 ****************************************************************************/

VOID PRIVENTRY VKModifyNormalKey(register PKKEY pKey)
{
  static BYTE abExtScan[] =
  {
    EXTSCAN_ALTESC,EXTSCAN_ALTBKSP,EXTSCAN_ALTLB,EXTSCAN_ALTRB,EXTSCAN_ALTHOME,
    EXTSCAN_ALTSEMI,EXTSCAN_ALTSQ,EXTSCAN_ALTBQ,EXTSCAN_ALTBSLASH,
    EXTSCAN_ALTCOMMA,EXTSCAN_ALTPERIOD,EXTSCAN_ALTSLASH,EXTSCAN_ALTKPMULT,
    EXTSCAN_ALTKPMINUS,EXTSCAN_CENTER,EXTSCAN_ALTKPPLUS,
  };

  register INT i;
  if (pKey->key_chChar == 0)
  {
    if (pKey->key_chScan < EXTSCAN_F11)
    {
      for (i = 0; i < sizeof(abExtScan); i++)
      {
        if (pKey->key_chScan == abExtScan[i])
        {
          pKey->key_chChar = 0xF0;
          break;
        }
      }
    }
  }
}
                                     /* VKModifyNormalKey                  */
#pragma  END_SWAP_CODE
