#define SCCSID "@(#)vdmint13.c  6.8 92/05/01"

/***    VDMINT13.C
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Title:
 *
 *      Initialize/Service for Mini-VDM for INT 13 DASD I/O
 *
 *      Description:
 *
 *
 *      Modification History:
 *
 *      Created:
 *
 */
/******************************************************************************
 * Change Log                                                                 *
 *                                                                            *
 * Mark    Date      Programmer  Comment                                      *
 * @3930   03/02/92  GAG         Fix the Internal error in the VDM manager.   *
 ******************************************************************************/
#define FNO_NUM  FNO_VDMINT13_C
#include <fnomvdm.h>

#define INCL_NONE
#define INCL_TYPES
#define INCL_MVDMTYPES
#define INCL_VDHALL
#define VDDSTRICT

#include <vdmvdh.h>
#include <kmode.h>
#include <kutil.h>
#include <sched.h>
#include <vdmbios.h>
#include <em86.h>

#include <vdmint13.h>
#include <vdmcsr.h>

#define MAKEULONG(l, h)  ((ULONG)(((USHORT)(l)) | ((ULONG)((USHORT)(h))) << 16))

UCHAR   MiniVDMCreate;
USHORT  Int13TCBNumber;

extern  struct          infoseg_s Lis2;

extern  HVDM            vdmid_HVDM;
extern  ULONG           vdmid_VDMSIG;
extern  LSYSVAL         vdmid_aLSV[];
extern  ULONG           vdmid_RMSIZE;
extern  PTCB            vdmid_mphvdmptcb;
extern  PPTDA           vdmid_mphvdmpptda;
extern  HPFH            vdmid_hpfh;
extern  LSYSVAL         vdmid_aLSV[];

extern  ULONG           cbKrnlInstance;
extern  ULONG           cbKrnlInstSwap;


//
// Existing services shared with the VDM code
//
extern VOID  PRIVENTRY  vdmmMarkVDM(VOID);
extern VOID             tkSchedNext();

//
// Interrupt Manager Services
//
extern VOID  PASCAL     INTSHUTOFFVDMIRQ(ULONG);
extern VOID  PASCAL     INTEOIVDMIRQ(ULONG);

//
// Save areas for EISA/uCHANNEL Interrupt handling
//
ULONG VDMInt13IRQEIP;
ULONG VDMInt13IRQCS;
ULONG VDMInt13CurIRQ;

//
// Bus Type Values
//
#define BUSTYPE_ISA     0l

//
// Service routines specific to the Int 13 mini-VDM implementation
//
extern VOID             vdmmEnterV86Mode();
extern VOID             vdmmInt13SVCCode();
extern ULONG            vdmmInt13BufferAlloc(PUSHORT,ULONG);
extern VOID             vdmmInt13BufferFree(USHORT);
extern ULONG            vdmmReadABIOSSelTbl(ULONG,PUSHORT);

typedef struct _ABTBLENTRY
{

  ULONG         ABTblSel;       // Selector
  ULONG         ABTblSeg;       // Segment
  ULONG         ABTblLimit;     // Limit
  ULONG         ABTblAccess;    // Access Rights

} ABTBLENTRY;


//
// Entry points callable from the SVC manager
//
ULONG CDECL     VDMInt13Request();
ULONG CDECL     VDMInt13IRQDone();

//
// Entry points callable from the DiskDD
//
VOID  VDDENTRY  VDMInt13TmrTick();
VOID  VDDENTRY  VDMInt13IRQRequest();

//
// Called via CONTEXT HOOK
//
VOID  HOOKENTRY VDMInt13IRQSimulate(PULONG,PCRF);

//
// Called via DevHlp_CreateInt13VDM
//
VOID  CDECL     VDMInt13CreateMiniVDM();
ULONG CDECL     VDMInt13CreateMiniVDMSub();
VOID  CDECL     VDMInt13DestroyMiniVDM(USHORT Flags);

static ULONG CDECL STRncmp(PBYTE d, PBYTE s, ULONG n);

//
// Pointer to the DiskDD <> Int 13 communication area. This
// pointer is initialized via DevHlp_CreateInt13VDM prior to
// calling the VDM creation routine.
//
//
PVDMINT13       pVDMInt13;

//
// These are the default (initial) registers for the miniVDM.
//
//
#define VDMINT13_DEFAULT_EFLAG  (F_INTERRUPT | F_IOPL3 | F_VM)
#define VDMINT13_DEFAULT_ESP    (ULONG)(4*1024-4)
#define VDMINT13_DEFAULT_SS     0l
#define VDMINT13_DEFAULT_EIP    (ULONG)(2*1024)
#define VDMINT13_DEFAULT_CS     0l
#define VDMINT13_DEFAULT_DS     0l

#define VDMINT13_IRQ_CS         VDMINT13_DEFAULT_CS
#define VDMINT13_IRQ_EIP       (VDMINT13_DEFAULT_EIP+16)

//
// MiniVDM Creation flags
//
// These flags indicate how far initialization has gone so
// resources can be deallocated incase of failure.
//
#define VDM_FIXED_ALLOCATED             0x0001
#define VDM_SWAP_ALLOCATED              0x0002
#define VDM_CONTEXT_ALLOCATED           0x0004
#define VDM_IOBUF_ALLOCATED             0x0008
#define VDM_IOBUF_MAPPED                0x0010
#define VDM_EXTBIOS_MAPPED              0x0020
#define VDM_BIOSROM_MAPPED              0x0040
#define VDM_PG0_MAPPED                  0x0080
#define VDM_TSS_CREATED                 0x0100
#define VDM_ABIOS_MAPPED                0x0200

#define BIOSINT_INT13                   0x13

//
// Minimum-Maximum I/O Buffer Size
//
#define MAX_IOBUF_SIZE                  64*1024l
#define MIN_IOBUF_SIZE                  16*1024l

static ULONG threads_ready;

static ULONG SelMin;
static ULONG SelMax;


#pragma BEGIN_MVDM_INIT_CODE
//
// Int 13 MiniVDM Creation
// ------------------------
//
// This routine gets control after the PTDA and first thread
// of the Int 13 MiniVDM have been created. This routine
// complete the 'in-context' part of the VDM initialization.
//
// Once initialization is complete, this thread becomes the
// VDM service thread. Control should end up in VDMInt13Request()
// as a result of an SVC_INT13 instruction in the mini-VDM.
//
// If initialization fails, a callout is made to the Disk DD
// (which is waiting for initialization to complete) and then
// the PTDA and resources for this thread are deleted.
//

VOID CDECL VDMInt13CreateMiniVDM()
{
  if ( !(pVDMInt13->Int13CreateRc = VDMInt13CreateMiniVDMSub()) )
    {
      pVDMInt13->Int13CreateRc = 0;
      vdmmEnterV86Mode();
    }
  else
    {
      pVDMInt13->Int13CreateRc = -1;
      pVDMInt13->Int13Functions[INT13_FCN_DISKDDCALLOUT]();
      pVDMInt13 = 0;
      while (1) tkSchedNext();
    }
}

ULONG CDECL VDMInt13CreateMiniVDMSub()
{

  VMAC        vmac;
  PBYTE       pDst,pSrc;
  PBYTE       pLinBuf;
  USHORT      SelBuf;
  ULONG       vpBuf;
  ULONG       rc;
  USHORT      Flags = 0;
  VMMAP       vmmap;
  VMSRC       vmsrc;
  ULONG       BufSize;
  ULONG       cb;
  ULONG       TblIndx;
  ABTBLENTRY  TblEntry;
  ULONG       SelStart;
  ULONG       SelEnd;

  ULONG       BIOSVec;
  PCSRMAP     pCSRMap;
  PCSROBJ     pCSRObj;
  ULONG       CSRCount;
  ULONG       i;


#define VMFLAGS1 VMAC_VDM | VMAC_LOCSPECIFIC
#define VMFLAGS2 VMAC_ARENAPRV
#define PGFLAGS  PG_W | PG_R

//
// Allocate areas VDM manager expects to be there
//
//
  vmac.ac_va = MVDM_INST_DATA_ADDR;
  cb         = ROUND_UP(cbKrnlInstance,4);

  if (rc=VMAllocMem(cb, cb, VMFLAGS1 | VMFLAGS2 | PGFLAGS | PG_RESIDENT,
               HOBNULL, (VMHOB)VDDHEAPOWNER, HOBNULL, NA, NA, SSToDS(&vmac)))
    {
      goto KillMiniVDM;
    }
  Flags |= VDM_FIXED_ALLOCATED;

  memset((PBYTE) vmac.ac_va, 0, cb);

  vmac.ac_va = MVDM_SINST_DATA_ADDR;
  cb         = ROUND_UP(cbKrnlInstSwap,4);


  if(rc=VMAllocMem(cb, cb, VMFLAGS1 | VMFLAGS2 | PGFLAGS | PG_RESIDENT,
           HOBNULL, (VMHOB)VDDHEAPOWNER, HOBNULL, NA, NA, SSToDS(&vmac)) )
    {
      goto KillMiniVDM;
    }
  Flags |= VDM_SWAP_ALLOCATED;

  memset((PBYTE) vmac.ac_va, 0, cb);

//
// Set-up the VDM address space
//
//
  if (rc=PGInitVDMContext(pPTDACur, (PULONG)(&vdmid_HVDM)))
    {
      goto KillMiniVDM;
    }
  Flags |= VDM_CONTEXT_ALLOCATED;

//
// KMExitKModeEvents will check this field to determine whether
// this thread is running. If so 'Global Actions' will not be
// scheduled to prevent deadlocks with the swapper.
//
  Int13TCBNumber = pTCBCur->TCBNumber;
//
// Set priority for this Int 13 VDM as indicated.
//
  pTCBCur->TCBPriClass = pVDMInt13->Int13PriClass;
  pTCBCur->TCBPriLevel = pVDMInt13->Int13PriLevel;
  TKUpdatePriority(pTCBCur);

//
// Initialize MVDM instance variables.
//
  vdmid_RMSIZE = 639;
  vdmid_mphvdmptcb  = pVDMInt13->Int13pTCB = pTCBCur;        // Current TCB
  vdmid_mphvdmpptda = pPTDACur;                              // Current PTDA
  vdmmAssertEQ(LSVSTART,VDHLSV_HVDM);
  vdmid_aLSV[VDHLSV_PRIORITYCLASS-LSVSTART].lsv_pv = &pTCBCur->TCBPriClass;
  vdmid_aLSV[VDHLSV_PRIORITYLEVEL-LSVSTART].lsv_pv = &pTCBCur->TCBPriLevel;
  vdmid_aLSV[VDHLSV_PCRF-LSVSTART].lsv_pv = &pTCBCur->TCB_pFrameBase;

  // Initialize page fault handling
  vdmid_hpfh.hf_ppfhFirst   = vdmid_hpfh.hf_ppfhLast = NULL;

  vdmmMarkVDM();
  vdmid_VDMSIG = HVDM_SIGNATURE;
//
// Map Real IVT into the VDM
//
// This is required for bus master cards which may use the 40:xxxx area
// for buffers to communicate with their cards.
//
//
  vmmap.map_laddr = 0x00000;
  vmmap.map_cpg   = 1;
  vmmap.map_har   = NULL;
  vmmap.map_x     = NULL;

  vmsrc.src_paddr = 0x00000;
  vmsrc.src_har   = NULL;

  if (rc=VMMapVDM( VMVDM_UVIRT, SSToDS(&vmmap), SSToDS(&vmsrc)) )
    {
      goto KillMiniVDM;
    }

  Flags |= VDM_PG0_MAPPED;
//
//
// Map the ABIOS Patch Area (if any) into the VDM
//
//
   SelMin  = SelMax = 0;

   TblIndx = 0;
   while ( vdmmReadABIOSSelTbl(TblIndx, SSToDS(&TblEntry)) )
     {
       SelStart = (TblEntry.ABTblSeg << 4) & ~0x00000fff;
       SelEnd   = (TblEntry.ABTblSeg << 4) + TblEntry.ABTblLimit;
       SelEnd  &= ~0x00000fff;

       if ( SelEnd < 0x9e000 && SelStart > 0x1000 )
         {
           if ( !SelMin || SelMin > SelStart ) SelMin = SelStart;
           if ( !SelMax || SelMax < SelEnd   ) SelMax = SelEnd;
         }
      TblIndx++;
     }

   if ( SelMin )
     {
       vmmap.map_laddr = SelMin;
       vmmap.map_cpg   = ((SelMax - SelMin) >> 12) + 1;
       vmmap.map_har   = NULL;
       vmmap.map_x     = NULL;

       vmsrc.src_paddr = SelMin;
       vmsrc.src_har   = NULL;

       if (rc=VMMapVDM( VMVDM_UVIRT, SSToDS(&vmmap), SSToDS(&vmsrc) ))
         {
           goto KillMiniVDM;
         }
       Flags |= VDM_ABIOS_MAPPED;
     }

//
// Map Real Extended BIOS data area IVT into the VDM
//
// This is required for bus master cards which may use the Ext BIOS area
// for buffers to communicate with their cards.
//
//
  vmmap.map_laddr = 0x9E000;   /* @3930 */
  vmmap.map_cpg   = 2;         /* @3930 */
  vmmap.map_har   = NULL;
  vmmap.map_x     = NULL;

  vmsrc.src_paddr = 0x9E000;   /* @3930 */
  vmsrc.src_har   = NULL;

  if (rc=VMMapVDM( VMVDM_UVIRT, SSToDS(&vmmap), SSToDS(&vmsrc) ))
    {
      goto KillMiniVDM;
    }
  Flags |= VDM_EXTBIOS_MAPPED;

/*
 * Map BIOS area into VDM
 *
 */
  vmmap.map_laddr = 0xa0000;
  vmmap.map_cpg   = 0x60000 >> 12;
  vmmap.map_har   = NULL;
  vmmap.map_x     = NULL;

  vmsrc.src_paddr = 0xa0000;
  vmsrc.src_har   = NULL;

  if (rc=VMMapVDM(VMVDM_UVIRT, SSToDS(&vmmap), SSToDS(&vmsrc)) )
    {
      goto KillMiniVDM;
    }
  Flags |= VDM_BIOSROM_MAPPED;


  // Allocate transfer buffer
  for (BufSize=MAX_IOBUF_SIZE; BufSize >= MIN_IOBUF_SIZE; BufSize-=4096l )
    {
      if ( pLinBuf=(PSZ) vdmmInt13BufferAlloc(SSToDS(&SelBuf), BufSize) )
        {
          pVDMInt13->Int13IOBufSectors = (USHORT) (BufSize >> 9);
          Flags |= VDM_IOBUF_ALLOCATED;
          break;
        }
    }
  if ( !(Flags & VDM_IOBUF_ALLOCATED) )
    {
      goto KillMiniVDM;
    }

  pVDMInt13->pInt13BufLin = (ULONG) pLinBuf;
  pVDMInt13->pInt13BufProt = MAKEULONG(0,SelBuf);
  vpBuf = ((ULONG)pLinBuf & 0x000F) | (((ULONG)pLinBuf&0xFFFF0)<<12);
  pVDMInt13->pInt13BufReal = (ULONG)vpBuf;

/*
 * Map transfer buffer into VDM (64K)
 *
 */
  vmmap.map_laddr = pVDMInt13->pInt13BufLin;
  vmmap.map_cpg   = BufSize >> 12;
  vmmap.map_har   = NULL;
  vmmap.map_x     = NULL;

  vmsrc.src_paddr = pVDMInt13->pInt13BufLin;
  vmsrc.src_har   = NULL;

  if (rc=VMMapVDM( VMVDM_UVIRT, SSToDS(&vmmap), SSToDS(&vmsrc) ) )
    {
      goto KillMiniVDM;
    }
  Flags |= VDM_IOBUF_MAPPED;


//
//
// Parallan Specific Enabling
//
//
//
//
  BIOSVec     = *(PULONG) (CSR_BIOSINT << 2);
  pCSRMap     = (PCSRMAP) (((BIOSVec >> 12) & 0x0ffff0) + (BIOSVec & 0x0ffffl));
  //
  //  Note: If the CSR vector (INT 60) is zero, we assume we do not
  //        have a valid mapping. Additional validation checks may
  //        be required.
  //
  if ( pCSRMap )
    {
      vmmap.map_laddr = (ULONG) pCSRMap;
      vmmap.map_cpg   = 1;
      vmmap.map_har   = NULL;
      vmmap.map_x     = NULL;

      vmsrc.src_paddr = (ULONG) pCSRMap;
      vmsrc.src_har   = NULL;
      //
      //
      // Note: We assume if this mapping fails, the failure is
      //       due to conflicts with a pre-existing mapping and
      //       that it access to the address range is permitted.
      //
      //
      rc = VMMapVDM( VMVDM_UVIRT, SSToDS(&vmmap), SSToDS(&vmsrc) );

      if ( !STRncmp( pCSRMap->signature,
                     CSR_SIGNATURE,
                     sizeof(pCSRMap->signature) ) )
        {
          CSRCount = pCSRMap->objCount;
          pCSRObj  = pCSRMap->objArray;

          vmmap.map_laddr = CSR_BASE_ADDRESS;

          for ( i = 0; i < CSRCount; i++, pCSRObj++ )
           {
             vmsrc.src_paddr = pCSRObj->physAddr;
             vmmap.map_cpg   = pCSRObj->objLength >> 12;
             vmmap.map_har   = NULL;
             vmmap.map_x     = NULL;
             vmsrc.src_har   = NULL;

             if ( !(rc=VMMapVDM(VMVDM_UVIRT, SSToDS(&vmmap), SSToDS(&vmsrc))) )
               {
                 pCSRObj->objMapping  = ((vmmap.map_laddr & 0xffff0l) << 12);
                 vmmap.map_laddr     += pCSRObj->objLength;
               }

           }
        }
      else
        {
          //
          // Note: If the mapping was successful. Then assume we had
          //       exclusive ownership of the area.
          //
          if ( !rc )
            {
              VMFreeMem((ULONG) pCSRMap, HOBNULL, VMFM_VDM );
            }
        }
    }

/*
 *  Set flag to generate I/O permission for all ports
 *
 */
  MiniVDMCreate = 1;
  rc=EM86CreateVDM();
  MiniVDMCreate = 0;
  if ( rc )
    {
      goto KillMiniVDM;
    }
  Flags |= VDM_TSS_CREATED;


/*
 * Allocate context hooks
 *
 *   - Simulate IRQ requests
 *   - Update BIOS timer count
 *
 */
  pVDMInt13->Int13IRQCtxHook = VDHAllocHook( VDH_CONTEXT_HOOK,
                                             (PFNARM) VDMInt13IRQSimulate,
                                             2*sizeof(ULONG) );

  pVDMInt13->Int13hvdm       = vdmid_HVDM;
  pVDMInt13->Int13Functions[INT13_FCN_SIMULATEIRQ] = VDMInt13IRQRequest;
  pVDMInt13->Int13Functions[INT13_FCN_SIMULATETMR] = VDMInt13TmrTick;
  pVDMInt13->Int13IRQPending = 0;

//
//
// Setup INITIAL V86 Registers
//
  pVDMInt13->Int13Regs.kmreg_eflag = VDMINT13_DEFAULT_EFLAG;
  pVDMInt13->Int13Regs.kmreg_esp   = VDMINT13_DEFAULT_ESP;
  pVDMInt13->Int13Regs.kmreg_ss    = VDMINT13_DEFAULT_SS;
  pVDMInt13->Int13Regs.kmreg_eip   = VDMINT13_DEFAULT_EIP;
  pVDMInt13->Int13Regs.kmreg_cs    = VDMINT13_DEFAULT_CS;
  pVDMInt13->Int13Regs.kmreg_ds    = VDMINT13_DEFAULT_DS;

  // copy code into VDM space
  pDst = (PSZ)(VDMINT13_DEFAULT_EIP + (VDMINT13_DEFAULT_CS<<4));
  pSrc = (PSZ) vdmmInt13SVCCode;
  memcpy( pDst, pSrc, 32);

  KMSetAllClientRegisters(pTCBCur, &pVDMInt13->Int13Regs);

  return(rc);

KillMiniVDM:
  VDMInt13DestroyMiniVDM(Flags);
  return(rc);
}

//
// Int 13 MiniVDM UnCreation
// -------------------------
//
// This routine releases resources in case the creation of
// the Int 13 miniVDM fails.
//
//
VOID CDECL VDMInt13DestroyMiniVDM(USHORT Flags)
{

  if ( Flags & VDM_PG0_MAPPED)
    {
      VMFreeMem(0l, HOBNULL, VMFM_VDM );
    }

  if ( Flags & VDM_BIOSROM_MAPPED)
    {
      VMFreeMem(0xA0000l, HOBNULL, VMFM_VDM );
    }

  if ( Flags & VDM_EXTBIOS_MAPPED)
    {
      VMFreeMem(0x9E000l, HOBNULL, VMFM_VDM );
    }

  if ( Flags & VDM_ABIOS_MAPPED)
    {
      VMFreeMem(SelMin, HOBNULL, VMFM_VDM );
    }

  if ( Flags & VDM_IOBUF_MAPPED)
    {
      VMFreeMem(pVDMInt13->pInt13BufLin, HOBNULL, VMFM_VDM );
    }

  if ( Flags & VDM_IOBUF_ALLOCATED)
    {
      vdmmInt13BufferFree(HIUSHORT(pVDMInt13->pInt13BufProt));
    }

  if ( Flags & VDM_TSS_CREATED)
    {
      EM86DestroyVDM();
    }
  if ( Flags & VDM_FIXED_ALLOCATED)
    {
      VMFreeMem(MVDM_INST_DATA_ADDR, HOBNULL, VMFM_VDM );
    }

  if ( Flags & VDM_SWAP_ALLOCATED )
    {
      VMFreeMem(MVDM_SINST_DATA_ADDR, HOBNULL, VMFM_VDM );
    }

  _disable();
  pPTDACur->ptda_infoseg.LIS_ProcType = LIS_PT_FULLSCRN;
  ((struct InfoSegLDT *)(Lis2.is_va))->LIS_ProcType = LIS_PT_FULLSCRN;
  _enable();

  Int13TCBNumber = 0;

  TKDieSoonPTDA( pPTDACur, -1, TC_NORMAL, 0l );

  return;
}

static ULONG CDECL STRncmp( PBYTE d, PBYTE s, ULONG n )
{
  ULONG cmp = 1;

  while( n-- && (cmp = (*d++ == *s++)) )
    ;

  return( !cmp );
}


#pragma END_MVDM_INIT_CODE

#pragma BEGIN_MVDM_GLOBAL_CODE

//
// Int 13 MiniVDM Service Routine
// -----------------------------
//
// This VDM thread is normally blocked in the DiskDD. When
// the DiskDD needs a BIOS operation, it unblocks this thread
// which returns to this routine. This routine 'PUSHES' an
// INT13 request into the client stack frame and returns
// control to the VDM SVC manager which return to V86 mode
// to execute the "PUSHED" Int13 operation. When the BIOS
// completes it returns to an SVC instruction which causes
// V86 mode to be exited. Control is returned to this routine
// via the SVC handler, which after collecting the client's
// register calls the DiskDD with the results of the operation.
//

ULONG CDECL VDMInt13Request()
{

  ULONG RequestEIP;
  ULONG RequestCS;

   while (1)
     {
       KMQueryClientRegister(pTCBCur, KM_REG_EIP, SSToDS(&RequestEIP) );
       KMQueryClientRegister(pTCBCur, KM_REG_CS,  SSToDS(&RequestCS)  );

       if ( RequestCS == VDMINT13_IRQ_CS && RequestEIP == VDMINT13_IRQ_EIP+3 )
         {
           return(VDMInt13IRQDone());
         }


       switch (pVDMInt13->Int13State)
         {

            case VDMINT13_START:
              // Start-up an Int 13 Request
              //  1.) Set-up client (V86) regs
              //  2.) Start the mini-VDM by returning to the VDM SVC manager.

              pVDMInt13->Int13State            = VDMINT13_COMPLETE;
              pVDMInt13->Int13Regs.kmreg_eflag = VDMINT13_DEFAULT_EFLAG;
              pVDMInt13->Int13Regs.kmreg_ds    = VDMINT13_DEFAULT_DS;
              KMSetAllClientRegisters(pTCBCur, &pVDMInt13->Int13Regs);
              VDHPushInt(BIOSINT_INT13);

              return(pVDMInt13->Int13Regs.kmreg_eax); //VSF(RAX) =
              break;                                  //   Function value

            case VDMINT13_COMPLETE:
              // Control has been returned from INT_13 miniVDM
              //
              //  1.) Tranfer VDM regs to shared memory block,
              //  2.) Notify Disk DD of the completion.
              //  3.) This Disk DD will either provide more work or will
              //      block this thread.
              //
              //  Note: The VDM automatically runs when initialized. The
              //        Int13State will be VDMINT13_IDLE. The Disk DD
              //        will be notified to indicate that the VDM has
              //        been initialized and is ready to accept requests.

              KMQueryAllClientRegisters(pTCBCur, &pVDMInt13->Int13Regs);

            case VDMINT13_IDLE:
              pVDMInt13->Int13Functions[INT13_FCN_DISKDDCALLOUT]();
              break;

            default:
              pVDMInt13->Int13State = VDMINT13_IDLE;
         }
     }
}

//
// Int 13 MiniVDM Tick Handler
// ---------------------------
//
// The DiskDD provides timer ticks for this VDM.
//
// When the DiskDD services a timer tick, it calls this
// routine which is pointed to by the VDMINT13 Function
// Table. This routine updates only updates the tick count
// and does not actually reflect the tick to the VDM. This
// appears to be sufficient for most Disk BIOS ROMs which
// poll for timeouts by checking this field.
//

VOID VDDENTRY VDMInt13TmrTick()
{
  if( !pVDMInt13->Int13IRQPending )
    {
      REFHVDM(pVDMInt13->Int13hvdm,ULONG,VDMBase.rb_ulTickCount)+=6;
    }
}

//
// Int 13 MiniVDM Interrupt Reflection
// -----------------------------------
//
// The DiskDD fields interrupts for this VDM.
//
// When the DiskDD receives an interrupt, it cannot reflect it
// directly to the VDM since the code is running in an interrupt
// context. Instead, it arms a "CONTEXT HOOK" specific to the
// mini-VDM context. When, the VDM is about to run, the expected
// IRQ is 'PUSHED' onto the VDM stack.
//

VOID VDDENTRY VDMInt13IRQRequest( ULONG IRQLevel, ULONG BusType )
{
  PULONG pIRQLevel;

  if ( BusType != BUSTYPE_ISA )
    {
      INTSHUTOFFVDMIRQ( IRQLevel );
    }

  if ( !pVDMInt13->Int13IRQPending )
    {
      pVDMInt13->Int13IRQPending = 1;
      VDHArmContextHook(pVDMInt13->Int13IRQCtxHook, pVDMInt13->Int13hvdm);
      pIRQLevel= (PULONG)VDHQueryHookData(pVDMInt13->Int13IRQCtxHook);
      *pIRQLevel     = IRQLevel;
      *(pIRQLevel+1) = BusType;
    }
}

VOID HOOKENTRY VDMInt13IRQSimulate( PULONG pIRQLevel, PCRF pCRF )
{
  ULONG IRQLevel = *pIRQLevel;
  ULONG BusType  = *(pIRQLevel+1);

  pCRF;

  VDMInt13CurIRQ = IRQLevel;

  if ( BusType != BUSTYPE_ISA )
    {
      KMQueryClientRegister(pTCBCur, KM_REG_EIP, &VDMInt13IRQEIP );
      KMQueryClientRegister(pTCBCur, KM_REG_CS,  &VDMInt13IRQCS  );

      KMSetClientRegister(pTCBCur, KM_REG_EIP, VDMINT13_IRQ_EIP );
      KMSetClientRegister(pTCBCur, KM_REG_CS,  VDMINT13_IRQ_CS  );
    }
  else
    {
      pVDMInt13->Int13IRQPending = 0;
    }

  VDHPushInt(IRQLevel + ((IRQLevel < 8) ? 0x08 : (0x70 - 0x08)) );
}


ULONG CDECL VDMInt13IRQDone()
{
  ULONG ClientEAX;

  pVDMInt13->Int13IRQPending = 0;

  INTEOIVDMIRQ( VDMInt13CurIRQ );

  KMSetClientRegister(pTCBCur, KM_REG_EIP, VDMInt13IRQEIP );
  KMSetClientRegister(pTCBCur, KM_REG_CS,  VDMInt13IRQCS  );

  KMQueryClientRegister(pTCBCur, KM_REG_EAX,  SSToDS(&ClientEAX) );

  return ( ClientEAX );
}

#pragma END_MVDM_GLOBAL_CODE
