PCjs Machines

Home of the original IBM PC emulator for browsers.

Logo

MS Windows 3.0 DDK Virtual Device Adaptation Guide

The following document is from the Microsoft Programmer’s Library 1.3 CD-ROM.

Microsoft  Windows  - Virtual Device Adaptation Guide








────────────────────────────────────────────────────────────────────────────
        Microsoft (R) Windows (tm) - Virtual Device Adaptation Guide

      development tools for providing Microsoft Windows device support
                                VERSION 3.0
────────────────────────────────────────────────────────────────────────────


for the MS-DOS (R) or PC-DOS Operating System







Microsoft Corporation

Information in this document is subject to change without notice and does
not represent a commitment on the part of Microsoft Corporation. The
software described in this document is furnished under a license agreement
or nondisclosure agreement. The software may be used or copied only in
accordance with the terms of the agreement. It is against the law to copy
the software on any medium except as specifically allowed in the license or
nondisclosure agreement. No part of this manual may be reproduced or
transmitted in any form or by any means, electronic or mechanical, including
photocopying and recording, for any purpose without the express written
permission of Microsoft.   U.S. Government Restricted Rights  The SOFTWARE
and documentation are provided with RESTRICTED RIGHTS. Use, duplication, or
disclosure by the Government is subject to restrictions as set forth in
subparagraph (c) (1) (ii) of the Rights in Technical Data and Computer
Software clause at DFARS 252.227-7013 or subparagraphs (c) (1) and (2) of
the Commercial Computer Software ─ Restricted Rights at 48 CFR 52.227-19, as
applicable. Contractor/manufacturer is Microsoft Corporation/One Microsoft
Way/Redmond, WA 98052-6399.  (C) Copyright Microsoft Corporation, 1990. All
rights reserved. Simultaneously published in the U.S. and Canada.
Printed and bound in the United States of America.


Microsoft, MS, MS-DOS, GW-BASIC, QuickC, CodeView, the
Microsoft logo, and XENIX are registered trademarks and Windows,
Windows/286,
and Windows/386 are trademarks of Microsoft Corporation.

Paintbrush is a registered trademark of Zsoft Corporation.

IBM, AT, and PS/2 are registered trademarks and PC/XT,
386, and Micro Channel are trademarks of International Business Machines
Corporation.

Intel is a registered trademark and i486 is a trademark
of Intel Corporation.

COMPAQ is a registered trademark of Compaq Computer Corporation.

Document No. SY0329C-300-R00-1089






Table of Contents
────────────────────────────────────────────────────────────────────────────



Introduction to Virtual Devices
     What You Should Know Before You Start
     Organization of This Document
     Notational Conventions


PART III  Writing Virtual Devices
────────────────────────────────────────────────────────────────────────────


Chapter 16  Overview of Windows in 386 Enhanced Mode

     16.1  The Operating Environment
     16.2  Virtual Machines
            16.2.1    The Privilege Rings of a VM
            16.2.2    VM Handles
            16.2.3    The Client Register Structure
     16.3  The Virtual Machine Manager
     16.4  Virtual Devices
            16.4.1    VxD Components
            16.4.2    The Device Control Procedure
            16.4.3    The Device Descriptor Block
            16.4.4    VxD IDs
            16.4.5    VxD Initialization Order
     16.5  How VxDs Work
            16.5.1    Enhanced Windows Execution Scheduling
            16.5.2    Memory Models
            16.5.3    Services
            16.5.4    Callback Procedures
            16.5.5    I/O Port Hooks
            16.5.6    Interrupt Hooks
            16.5.7    Loading Sequence
     16.6  VxD Examples

Chapter 17  Virtual Device Programming Topics

     17.1  Writing VxDs
            17.1.1    VxD Memory Model
            17.1.2    VxD Segmentation
            17.1.3    VxD Declaration
            17.1.4    VxD Services
            17.1.5    VxD APIs (Call-Ins)
            17.1.6    VxD INT 2F Usage
     17.2  Building a VxD
            17.2.1    MASM5.10B
            17.2.2    LINK386
            17.2.3    ADDHDR
            17.2.4    MAPSYM32
            17.2.5    Installing a VxD
     17.3  Initializing a VxD
            17.3.1    Real-Mode Initialization
            17.3.2    Protected-Mode Initialization
     17.4  Tracking the VM States
            17.4.1    VM Creation and Initialization
            17.4.2    VM State Changes
            17.4.3    VM Termination
     17.5  Exiting Windows
     17.6  Debugging a VxD
            17.6.1    Entering WDEB386
            17.6.2    Tracing Paths of Execution
            17.6.3    Querying the VxD State

Chapter 18  The VDD and Grabber DLL

     18.1  Introduction to VDDs and the Grabber DLL
            18.1.1    VDD Interaction with WINOLDAP
            18.1.2    VDD User Messages
            18.1.3    VDD I/O Trapping and Hooked Pages
            18.1.4    Grabber Naming Conventions
     18.2  VDD Programming
            18.2.1    VDD Efficiency
            18.2.2    Converting Your 2.x VDD
            18.2.3    Environment State Change Requirements for a VDD
            18.2.4    Grabber API
     18.3  Grabber DLL Interfaces
            18.3.1    On-Screen Selection Functions
            18.3.2    Selection Functions
            AdjustInitEndPt
            BeginSelection
            ConsSelecRec
            EndSelection
            InvertSelection
            KeySelection
            MakeSelctRect
            RenderSelection
            GetDisplayUpd
            CheckGRBVersion
            CursorOff
            CursorOn
            CursorPosit
            GetFontList
            GrabComplete
            GrabEvent
            GrbUnLockApp
            InitGrabber
            PaintScreen
            ScreenFree
            SetPaintFnt
            UpdateScreen


PART IV  Virtual Device Services
────────────────────────────────────────────────────────────────────────────


Chapter 19  Memory Management Services

     19.1  System Data Object Management
            Allocate_Device_CB_Area
            Allocate_Global_V86_Data_Area
            Allocate_Temp_V86_Data_Area
            Free_Temp_V86_Data_Area
            Device V86 Page Management
            Assign_Device_V86_Pages Assign_Device_V86_Pages service
            Deassign_Device_V86_Pages
            Get_Device_V86_Pages_Array
            Hook_V86_Page
            GDT/LDT Management
            Allocate_GDT_Selector
            Allocate_LDT_Selector
            BuildDescDWORDs
            Free_GDT_Selector
            Free_LDT_Selector
            GetDescriptor
            SetDescriptor
            System Heap Allocator
            HeapAllocate
            HeapFree
            HeapGetSize
            HeapReAllocate
            System Page Allocator
            CopyPageTable
            GetDemandPageInfo
            GetFreePageCount
            GetSetPageOutCount
            GetSysPageCount
            GetVMPgCount
            MapIntoV86
            ModifyPageBits
            PageAllocate
            PageFree
            PageGetAllocInfo
            PageGetSizeAddr
            PageLock
            PageOutDirtyPages
            PageReAllocate
            PageUnLock
            PhysIntoV86
            TestGlobalV86Mem
            Looking At Physical Device Memory From a VxD
            MapPhysToLinear
            Data Access Services
            GetFirstV86Page
            GetNulPageHandle
            GetAppFlatDSAlias()
            Special Services For Protected Mode APIs
            GetV86PageableArray
            LinMapIntoV86
            LinPageLock
            LinPageUnLock
            PageCheckLinRange
            PageDiscardPages
            SelectorMapFlat
            SetResetV86Pageable
            Instance Data Management
            AddInstanceItem
            MMGR_Toggle_HMA
            Looking At V86 Address Space
            CB_High_Linear

Chapter 20  I/O Services and Macros

     20.1  Handling Different I/O Types
     20.2  I/O Macros
     20.3  I/O Services
            Enable_Global_Trapping, Disable_Global_Trapping
            Enable_Local_Trapping, Disable_Local_Trapping
            Install_IO_Handler (Initialization only)
            Install_Mult_IO_Handlers (Initialization only)
            Simulate_IO

Chapter 21  VM Interrupt and Call Services

            Build_Int_Stack_Frame
            Call_When_Idle
            Call_When_VM_Ints_Enabled
            Disable_VM_Ints
            Enable_VM_Ints
            Get_PM_Int_Type
            Get_V86_Int_Vector, Get_PM_Int_Vector
            Hook_V86_Int_Chain (Initialization only)
            Set_PM_Int_Type
            Set_V86_Int_Vector, Set_PM_Int_Vector
            Simulate_Far_Call
            Simulate_Far_Jmp
            Simulate_Far_Ret
            Simulate_Far_Ret_N
            Simulate_Int
            Simulate_Iret

Chapter 22  Nested Execution Services

            Begin_Nest_Exec
            Begin_Nest_V86_Exec
            Begin_Use_Locked_PM_Stack
            End_Nest_Exec
            End_Use_Locked_PM_Stack
            Exec_Int
            Exec_VxD_Int
            Restore_Client_State
            Resume_Exec
            Save_Client_State
            Set_PM_Exec_Mode
            Set_V86_Exec_Mode

Chapter 23  Break Point and Callback Services

            Allocate_V86_Call_Back, Allocate_PM_Call_Back
            Call_When_Idle
            Call_When_VM_Returns
            Install_V86_Break_Point
            Remove_V86_Break_Point

Chapter 24  Primary Scheduler Services

            Adjust_Exec_Priority
            Begin_Critical_Section
            Call_When_Not_Critical
            Call_When_Task_Switched
            Claim_Critical_Section
            Create_Semaphore
            Destroy_Semaphore
            End_Crit_And_Suspend
            End_Critical_Section
            Get_Crit_Section_Status
            No_Fail_Resume_VM
            Nuke_VM
            Release_Critical_Section
            Resume_VM
            Signal_Semaphore
            Suspend_VM
            Wait_Semaphore

Chapter 25  Time-Slice Scheduler Services

            Adjust_Execution_Time
            Call_When_Idle
            Get_Execution_Focus
            Get_Time_Slice_Granularity
            Get_Time_Slice_Info
            Get_Time_Slice_Priority
            Release_Time_Slice
            Set_Execution_Focus
            Set_Time_Slice_Granularity
            Set_Time_Slice_Priority
            Wake_Up_VM

Chapter 26  Event Services

            Call_Global_Event
            Call_Priority_VM_Event
            Call_VM_Event
            Cancel_Global_Event
            Cancel_Priority_VM_Event
            Cancel_VM_Event
            Hook_NMI_Event
            Schedule_Global_Event
            Schedule_VM_Event

Chapter 27  Timing Services

            Cancel_Time_Out
            Get_Last_Updated_System_Time
            Get_Last_Updated_VM_Exec_Time
            Get_System_Time
            Get_VM_Exec_Time
            Set_Global_Time_Out
            Set_VM_Time_Out
            Update_System_Clock

Chapter 28  Processor Fault and Interrupt Services

            Get_Fault_Hook_Addrs
            Get_NMI_Handler_Addr
            Hook_NMI_Event
            Hook_V86_Fault, Hook_PM_Fault, Hook_VMM_Fault
            Hook_V86_Page
            Set_NMI_Handler_Addr

Chapter 29  Information Services

            Get_Cur_VM_Handle
            Get_Next_VM_Handle
            Get_Sys_VM_Handle
            Get_VMM_Reenter_Count
            Get_VMM_Version
            GetSet_HMA_Info
            Test_Cur_VM_Handle
            Test_Debug_Installed
            Test_Sys_VM_Handle
            Validate_VM_Handle

Chapter 30  Initialization Information Services

            Convert_Boolean_String (Initialization only)
            Convert_Decimal_String (Initialization only)
            Convert_Fixed_Point_String (Initialization only)
            Convert_Hex_String (Initialization only)
            Get_Config_Directory (Initialization only)
            Get_Environment_String (Initialization only)
            Get_Exec_Path (Initialization only)
            GetDOSVectors (Initialization only)
            Get_Machine_Info (Initialization only)
            Get_Next_Profile_String (Initialization only)
            Get_Profile_Boolean (Initialization only)
            Get_Profile_Decimal_Int (Initialization only)
            Get_Profile_Fixed_Point (Initialization only)
            Get_Profile_Hex_Int (Initialization only)
            Get_Profile_String (Initialization only)
            Get_PSP_Segment (Initialization only)
            OpenFile (Initialization only)

Chapter 31  Linked List Services

            List_Allocate
            List_Attach
            List_Attach_Tail
            List_Create
            List_Deallocate
            List_Destroy
            List_Get_First
            List_Get_Next
            List_Insert
            List_Remove
            List_Remove_First

Chapter 32  Error Condition Services

            Crash_Cur_VM
            Fatal_Error_Handler
            Fatal_Memory_Error

Chapter 33  Miscellaneous Services

            Begin_Reentrant_Execution
            End_Reentrant_Execution
            Hook_Device_Service
            Hook_Device_V86_API, Hook_PM_Device_API
            Map_Flat
            MMGR_SetNULPageAddr
            Set_System_Exit_Code
            Simulate_Pop
            Simulate_Push
            System_Control

Chapter 34  Shell Services

            SHELL_Event
            SHELL_Get_Version
            SHELL_Message
            SHELL_Resolve_Contention
            SHELL_SYSMODAL_Message

Chapter 35  Virtual Display Device (VDD) Services

     35.1  Displaying a VM's Video Memory in a Window
     35.2  Message Mode Services
            VDD_Msg_BakColor
            VDD_Msg_ClrScrn
            VDD_Msg_ForColor
            VDD_Msg_SetCursPos
            VDD_Msg_TextOut
            Miscellaneous VDD Services
            VDD_Get_GrabRtn
            VDD_Get_ModTime
            VDD_Get_Version
            VDD_Hide_Cursor
            VDD_PIF_State
            VDD_Query_Access
            VDD_Set_HCurTrk
            VDD_Set_VMType
            VDD_Query_Access

Chapter 36  Virtual Keyboard Device (VKD) Services

            VKD_Cancel_Hot_Key_State
            VKD_Cancel_Paste
            VKD_Define_Hot_Key
            VKD_Define_Paste_Mode
            VKD_Flush_Msg_Key_Queue
            VKD_Force_Keys
            VKD_Get_Kbd_Owner
            VKD_Get_Msg_Key
            VKD_Get_Version
            VKD_Local_Disable_Hot_Key
            VKD_Local_Enable_Hot_Key
            VKD_Peek_Msg_Key
            VKD_Reflect_Hot_Key
            VKD_Remove_Hot_Key
            VKD_Start_Paste

Chapter 37  Virtual PIC Device (VPICD) Services

     37.1  Default Interrupt Handling
     37.2  Virtualizing an IRQ
     37.3  Virtualized IRQ Callback Procedures
            VID_Hw_Int_Proc
            VID_EOI_Proc
            VID_Virt_Int_Proc
            VID_IRET_Proc
            VID_Mask_Change_Proc
            VPICD Services
            VPICD_Call_When_Hw_Int
            VPICD_Clear_Int_Request
            VPICD_Convert_Handle_To_IRQ
            VPICD_Convert_Int_To_IRQ
            VPICD_Convert_IRQ_To_Int
            VPICD_Get_Complete_Status
            VPICD_Get_IRQ_Complete_Status
            VPICD_Get_Status
            VPICD_Get_Version
            VPICD_Phys_EOI
            VPICD_Physically_Mask
            VPICD_Physically_Unmask
            VPICD_Set_Auto_Masking
            VPICD_Set_Int_Request
            VPICD_Test_Phys_Request
            VPICD_Virtualize_IRQ

Chapter 38  Virtual Sound Device (VSD) Services

            VSD_Bell
            VSD_Get_Version

Chapter 39  Virtual Timer Device (VTD) Services

            VTD_Begin_Min_Int_Period
            VTD_Disable_Trapping
            VTD_Enable_Trapping
            VTD_End_Min_Int_Period
            VTD_Get_Interrupt_Period
            VTD_Get_Version
            VTD_Update_System_Clock

Chapter 40  V86 Mode Memory Manager Device Services

     40.1  Initialization Services
            V86MMGR_Get_Version
            V86MMGR_Allocate_V86_Pages
            V86MMGR_Set_EMS_XMS_Limits
            V86MMGR_Get_EMS_XMS_Limits
            API Translation and Mapping
            V86MMGR_Set_Mapping_Info
            V86MMGR_Xlat_API
            V86MMGR_Load_Client_Ptr
            V86MMGR_Allocate_Buffer
            V86MMGR_Free_Buffer
            V86MMGR_Get_Xlat_Buff_State
            V86MMGR_Set_Xlat_Buff_State
            V86MMGR_Get_VM_Flat_Sel
            V86MMGR_Get_Mapping_Info
            V86MMGR_Map_Pages
            V86MMGR_Free_Page_Map_Region

Chapter 41  Virtual DMA Device (VDMAD) Services

            VDMAD_Copy_From_Buffer
            VDMAD_Copy_To_Buffer
            VDMAD_Default_Handler
            VDMAD_Disable_Translation
            VDMAD_Enable_Translation
            VDMAD_Get_EISA_Adr_Mode
            VDMAD_Get_Region_Info
            VDMAD_Get_Version
            VDMAD_Get_Virt_State
            VDMAD_Lock_DMA_Region
            VDMAD_Mask_Channel
            VDMAD_Release_Buffer
            VDMAD_Request_Buffer
            VDMAD_Reserve_Buffer_Space
            VDMAD_Scatter_Lock
            VDMAD_Scatter_Unlock
            VDMAD_Set_EISA_Adr_Mode
            VDMAD_Set_Phys_State
            VDMAD_Set_Region_Info
            VDMAD_Set_Virt_State
            VDMAD_Unlock_DMA_Region
            VDMAD_UnMask_Channel
            VDMAD_Virtualize_Channel

Chapter 42  Virtual DOSNET Device Services

            DOSNET_Get_Version
            DOSNET_Do_PSP_Adjust
            DOSNET_Send_FILESYSCHANGE

Appendix A  Appendix A Terms and Acronyms


Appendix B  Understanding Modes

     B.1   Windows Modes
     B.2   Microprocessor Modes

Appendix C  Creating Distribution Disks for Drivers

     C.1   What is an Information File?
     C.2   Different Types of Information Files
     C.3   General Format and Syntax for Information Files
     C.4   File Location Information
     C.5   Creating .INF File Entries
            C.5.1    Display Device
            C.5.2    Mouse or Other Pointing Device
            C.5.3    Network Device
            C.5.4    Keyboard Device
            C.5.5    Machine-Dependent Configuration
     C.6   Creating .INF File Entries for Printer Drivers
            C.6.1    .INF File Entries
            C.6.2    Driver Description (.DEF) File
            C.6.3    Special Considerations
     C.7   Testing the Installation of Your .INF File
            C.7.1    Testing with Setup
            C.7.2    Testing with Control Panel
     C.8   Informing Microsoft About the Availability of Your Driver
       and/or Virtual Device
     C.9   Error Messages When Running Debug Setup

Appendix D  Windows INT 2FH API

     D.1   Call-In Interfaces
            D.1.1    Enhanced Windows Installation Check (AX=1600H)
            D.1.2    Releasing Current Virtual Machine's Time-Slice
              (AX=1680h)
            D.1.3    Begin Critical Section (AX=1681h)
            D.1.4    End Critical Section (AX=1682h)
            D.1.5    Get Current Virtual Machine ID (AX=1683h)
            D.1.6    Get Device API Entry Point (AX=1684h)
            D.1.7    Switch VMs and CallBack (AX=1685h)
            D.1.8    Detect Presence of INT 31H Services (AX=1686h)
            Call Out Interfaces
            Windows/386 Version 2.xx API Compatibility

Index




Introduction to Virtual Devices
────────────────────────────────────────────────────────────────────────────

This document explains how to modify existing device drivers or create new
virtual devices that will work with Microsoft(R) Windows(tm) 3.0 when
running in 386(tm) enhanced mode.

This introduction provides some background information that you should
review before using this documentation. The topics are presented in the
following order:


  ■   What you should know before you start

  ■   Organization of this document

  ■   Notational conventions




What You Should Know Before You Start

To program virtual devices for Windows when running in 386 enhanced mode,
you should be familiar with Chapter 1, "Overview of Windows," in the
Microsoft Windows Device Driver Adaptation Guide and the following topics.
Suggested reference materials are shown by topic:

╓┌───────────────────────────────────────┌───────────────────────────────────►
Topic                                   Reference
────────────────────────────────────────────────────────────────────────────
Intel(R) 80386 and 80486 architecture   Introduction to the 80386, Intel
                                        Literature Sales P.O. Box 58130 Santa
                                        Clara, CA 95052-8130 Order Number:
                                        231252-001 ISBN 0-917017-38-2

                                        i486(tm) Microprocessor Programmer's
                                        Reference Manual, Intel Literature
                                        Sales P.O. Box 58130 Santa Clara, CA
                                        95052-8130 Order Number: 240486-001
                                        ISBN 1-5512-101-2

MS-DOS(R)                               Duncan, Ray. Advanced MS-DOS.
                                        Microsoft Press, P.O. Box 97017,
                                        Redmond WA. 98073-9717. ISBN Number:
                                        0-914845-77-2

                                        MS-DOS Encyclopedia. Microsoft Press,
                                        P.O. Box 97017, Redmond WA. 98073-971
                                        ISBN Number: 1-5565-174-8
Topic                                   Reference
────────────────────────────────────────────────────────────────────────────
                                        ISBN Number: 1-5565-174-8

Microsoft Windows 3.0 (especially the   Microsoft Windows Software Developmen
Memory Management topics)               Kit, Programming Topics



To obtain the DOS Protected Mode Interface (DPMI) specification, contact
Intel at 1-800-548-4725. For the Virtual DMA Services (VDS) specification,
submit a service request via Microsoft OnLine.


Organization of This Document

This document is divided into the following parts and chapters:

Part 3, "Writing Virtual Devices," describes the requirements of a virtual
device and the environment of Windows when running in 386 enhanced mode. It
contains the following chapters:


  ■   Chapter 16, "Overview of Windows in 386 Enhanced Mode," which provides
      the conceptual foundation of the Windows virtual machine environment.

  ■   Chapter 17, "Virtual Device Programming Topics," which provides a more
      in-depth look at various programming topics.

  ■   Chapter 18, "The VDD and Grabber DLL," which describes the development
      of a Virtual Display Driver (VDD) and the dynamic-link library (DLL)
      needed to support a video adapter.


Part 4, "Virtual Device Services," provides detailed descriptions of all the
available services. It consists of the following 24 chapters:


  ■   Chapter 19, "Memory Management Services"

  ■   Chapter 20, "I/O Services and Macros"

  ■   Chapter 21, "VM Interrupt and Call Services"

  ■   Chapter 22, "Nested Execution Services"

  ■   Chapter 23, "Break Point and Callback Services"

  ■   Chapter 24, "Primary Scheduler Services"

  ■   Chapter 25, "Time-Slice Scheduler Services"

  ■   Chapter 26, "Event Services"

  ■   Chapter 27, "Timing Services"

  ■   Chapter 28, "Processor Fault and Interrupt Services"

  ■   Chapter 29, "Information Services"

  ■   Chapter 30, "Initialization Information Services"

  ■   Chapter 31, "Linked List Services"

  ■   Chapter 32, "Error Condition Services"

  ■   Chapter 33, "Miscellaneous Services"

  ■   Chapter 34, "Shell Services"

  ■   Chapter 35, "Virtual Display Device (VDD) Services"

  ■   Chapter 36, "Virtual Keyboard Device (VKD) Services"

  ■   Chapter 37, "Virtual PIC Device (VPICD) Services"

  ■   Chapter 38, "Virtual Sound Device (VSD) Services"

  ■   Chapter 39, "Virtual Timer Device (VTD) Services"

  ■   Chapter 40, "V86 Mode Memory Manager Device Services"

  ■   Chapter 41, "Virtual DMA Device (VDMAD) Services"

  ■   Chapter 42, "Virtual DOSNET Device Services"


Part 5, "Appendixes," provides the following supplemental reference
material:


  ■   Appendix A, "Terms and Acronyms"

  ■   Appendix B, "Understanding Modes"

  ■   Appendix C, "Creating Distribution Disks for Drivers"

  ■   Appendix D, "Windows INT 2FH API"



Notational Conventions

The following notational conventions are used throughout the DDK
documentation set.

╓┌─────────────────────────────────┌─────────────────────────────────────────►
Convention                        Meaning
────────────────────────────────────────────────────────────────────────────
bold                              Bold is used for keywords, such as
                                  function, register, macro, and data
                                  structure field names. These names are
                                  spelled exactly as they should appear in
                                  source programs. Notice the bold in the
                                  following example:

                                  Disable (lpDestDev)

                                  Here, Disable is bold to indicate that it
                                  is the name of a function.

italics                           Italics are used to indicate a placeholder
                                  that should be replaced by an actual
                                  argument. In the preceding example,
                                  lpDestDev is italic to indicate that it
                                  should be replaced by an argument.
Convention                        Meaning
────────────────────────────────────────────────────────────────────────────
                                  should be replaced by an argument.

(parentheses)                     Parentheses enclose the parameter or
                                  parameters that are to be passed to a
                                  function. In the preceding example,
                                  lpDestDev is the parameter.

Monospace                         Monospace type is used for program code
                                  fragments and to illustrate the syntax of
                                  data structures.








PART III  Writing Virtual Devices
────────────────────────────────────────────────────────────────────────────

Microsoft Windows 3.0, while running in 386 enhanced mode, is a
single-threaded multitasking environment. It can run unmodified,
singletasking non-Windows applications by virtualizing system resources. A
resource should be virtualized when multiple applications might use the
resource. The software that does this, the virtual device, can also be used
to provide other services, both to applications and other virtual devices.

This part describes the enhanced Windows environment and details the writing
of virtual devices. Part 4, "Virtual Device Services," describes the
services provided by virtual devices. Chapter 1, "Overview of Windows," in
the Microsoft Windows Device Driver Adaptation Guide also contains material
of general interest to the virtual device author.






Chapter 16  Overview of Windows in 386 Enhanced Mode
────────────────────────────────────────────────────────────────────────────

When Microsoft Windows 3.0 is loaded and invoked on an appropriately
configured system, it runs in an "enhanced" mode designed to capitalize on
the power of the Intel 80386/80486 microprocessors. The 80386 and 80486
chips, support multiple, independent memory regions. Enhanced Windows uses
this to build multiple, independent virtual machines, each capable of
running an application program.

Enhanced Windows supports this multitasking virtual machine environment with
a sophisticated set of services that are provided by the virtual devices
(VxDs). Virtual devices provide access to all the system resources,
including memory management, scheduling, and all the hardware devices.

By writing a VxD for a particular hardware device, the author integrates
that device into the powerful, enhanced Windows environment. For instance, a
properly implemented virtual printer device will, by serializing access to
the hardware port, enable multiple applications to share a single printer.

This chapter provides a general description of the virtual machine
environment and introduces the components of a virtual device. However,
detailed programming instructions for the 80386 and 80486 are not provided.
Therefore, a VxD programmer should already be familiar with the topics
described in the preceding "Introduction to Virtual Devices."

In the September, 1987, issue of the Microsoft Systems Journal, the article
entitled "Microsoft Windows/386: Creating a Virtual Machine Environment,"
discusses the structure of Windows/386(tm) version 2.x. It also contains an
excellent description of the four modes of the Intel 80386 microprocessor. A
portion of that discussion is included in Appendix B, "Understanding Modes,"
to help you understand the current version of Windows when running in 386
enhanced mode.


16.1  The Operating Environment

Windows in 386 enhanced mode has a virtual machine (VM) architecture that
provides preemptive multitasking for DOS applications on the 80386 and 80486
processor.

The following three major components are also shown graphically in Figure
16.1:


  ■   Virtual machines

  ■   Virtual Machine Manager

  ■   Virtual devices


Windows 3.0 virtual machines (VMs) consist of a virtual 8086 (V86) mode
portion and, a optional protected-mode (PM) portion and a control block used
by the environment. The first VM created is called the System VM. This is
the virtual machine in which the Windows graphical user interface runs.
Non-Windows applications run in VMs of their own.

The Virtual Machine Manager (VMM) functions as a multitasking operating
environment. The VMM provides services that control the main memory, the CPU
execution time, and the peripheral devices. It runs, along with all the
VxDs, in one, flat-model, 32-bit memory segment.

The virtual devices (VxDs) either virtualize a peripheral device, provide
services for the VMM and VxDs, or both. The "x" in VxD stands for an
arbitrary device. In an actual device name, the "x" is replaced with the
name of the virtualized device, e.g., VDD for Virtual Display Device and
VDMAD for Virtual DMA Device.

Internal hardware devices (e.g., the Programmable Interrupt Controller) and
peripheral devices (e.g., a printer) are shown outside of the enhanced
Windows environment in Figure 16.1. However, this is a simplified drawing
since software routines such as BIOS may also be considered part of the
peripheral device.

(This figure may be found in the printed book).


16.2  Virtual Machines

When Windows is running in 386 enhanced mode, it creates memory partitions
that have a remarkable characteristic: programs that run within these
partitions execute as though they were running on an 80386 in real mode.
Each partition, called a virtual machine, comes complete with its own
address space, I/0 port space, and interrupt vector table. Multiple virtual
machines can be running simultaneously, with each under the illusion that it
is in complete control of the computer.

A VM initially runs in the Virtual-86 (V86) mode of the processor. In this
mode, the application running in the VM can make use of all the functions
associated with 8086 emulation and additional privileged instructions
provided by the 80386. The application can also use the protected mode
features of the 80386, but should do so via the DPMI (DOS Protected Mode
Interface) specification (available from Intel at 1-800-548-4725).

A virtual machine (VM) is a complete description of the state of an
application. Each VM includes the following:


  ■   The memory associated with the application

  ■   The processor registers

  ■   The data structures associated with virtualization


The data is used by the VMM and VxDs to virtualize the hardware and to
provide services. It is maintained in a data structure called the Control
Block. The processor registers are maintained on the VMM stack and can be
accessed via the Client Register Structure. The memory can be accessed and
manipulated by means of a number of VMM memory manager services.

The code for MS-DOS and the Windows portion common to the three product
modes (real, standard, and 386 enhanced) runs in the System VM. Most of
MS-DOS, including all the MS-DOS device drivers and TSRs, is usually shared
globally among the VMs.


16.2.1  The Privilege Rings of a VM

A VM can have more than one privilege ring. Code executing in one privilege
ring can only have access to memory in the same privilege ring or one with a
higher number (i.e., lower privilege level).

The virtual-8086 (V86) mode portion, shown in Figure 16.2, runs in privilege
ring 3. This is the code and data most typically associated with MS-DOS
applications.

The second part is a protected-mode (PM) portion that runs at privilege ring
1, 2, or 3. This portion can be used by applications running under enhanced
Windows. In the System VM (SYS VM), this portion is used to run the Windows
graphical interface.

The third part is code and data utilized by the VMM and the VxDs running at
privilege ring 0.

The ring 0 data has three subparts, each associated with a particular VM
(per VM):


  1.  The stack, which contains the Client Register Structure (CRS). The
      ring 0 stack is used by the VMM and VxDs when a VM is running.

  2.  The control block, which contains other data (i.e., values associated
      with the virtualization of hardware for a VM) local to a VM.

  3.  Data owned by a VxD, which contains information that maintains the
      state, such as the state of the physical hardware, across all VMs.

      (This figure may be found in the printed book).



16.2.2  VM Handles

Enhanced Windows virtual devices refer to specific VMs by VM handles. By
convention, VM handles are usually stored in the EBX register. A VM handle
is actually a 32-bit linear address of the virtual machine's control block
data structure.


16.2.3  The Client Register Structure

The Client Register Structure (CRS), as shown in Figure 16.3, contains the
virtual machine processor state including all the virtual machine's
registers and flags. When a device wants to look at or modify a virtual
machine's registers, it must modify the CRS.

(This figure may be found in the printed book).


16.3  The Virtual Machine Manager

The Virtual Machine Manager (VMM) is a device-independent layer of code that
provides a framework upon which the virtual devices build virtualizations of
physical devices or provide services for each of the VMs. In this sense, the
VMM lies between the VMs and the VxDs. All interaction between the software
running in the VMs and the VxDs occurs via the interface provided by the
VMM.

The VMM also provides a set of services that allows for creating,
destroying, running, synchronizing, and altering the state of the VMs. The
VMM, as shown in Figure 16.4, handles all the privilege ring transitions of
VMs to privilege ring 0, provides scheduling services, manages memory, and
provides services for such activities as trapping I/O and hooking software
interrupts.

(This figure may be found in the printed book).


16.4  Virtual Devices

Enhanced Windows virtual devices (VxDs) are the interfaces between
application software and the hardware. Most VxDs correspond to a hardware
device, though not all do. For example, the VxDs for printers and displays
simulate actual hardware interfaces, but the VxD called Shell provides
access to the Windows graphical user interface. VxDs use services provided
by the VMM and other VxDs.

VxDs can provide control functions, service functions, API functions, and
callback procedures that are used to virtualize, synchronize, and maintain
the state of the hardware for the VMs. A callback procedure is a request for
notification when a specified event occurs in the normal execution of the
application code.

There must be a VxD for each piece of hardware that can have a different
state in each of the VMs.


16.4.1  VxD Components

Installable virtual devices have the following five, distinct parts, which
are shown graphically in Figure 16.5:


  1.  Real-mode initialization code and data, which is discarded after
      loading parts 2 - 5

  2.  Protected-mode (PM) initialization code, which is discarded after
      initialization

  3.  Protected-mode (PM) initialization data, which is discarded after
      initialization

  4.  PM code, which contains the Device Control Procedure, API and callback
      procedures, and services

  5.  PM data, which contains the Device Descriptor Block, Service Table,
      and Global Data



16.4.2  The Device Control Procedure

The Device Control Procedure (DCP) is the dispatch point for most of the VMM
interaction with the VxD. Besides the initialization of the system, there
are device control calls for creating, initializing, and destroying VMs; for
setting the device focus to a VM; and for indicating a change in the state
of the VM.

The VMM broadcasts messages to all VxD DCPs indicating changes in the state
of the system or of a VM. The DCP can then modify the device's data
structure or the VM's state. The address of the DCP is specified in a
special data structure called a Device Descriptor Block that all virtual
devices must have. See Chapter 17, "Virtual Device Programming Topics," for
details on messages passed to the DCP.


16.4.3  The Device Descriptor Block

The Device Descriptor Block (DDB) is a VxD-unique data structure containing
the VxD's name, version IDs, and entry points for the three code areas: the
Device Control Procedure, V86-mode API procedure, and the PM API procedure.
In addition, the DDB can contain a pointer to a table of services provided
by the VxD. See Chapter 17, "Virtual Device Programming Topics, " for a
detailed description of a DDB.

(This figure may be found in the printed book).


16.4.4  VxD IDs

VxD IDs uniquely identify a VxD. They are a 16-bit combination of the OEM
number and the device number in the form:

  xOOOOOOOOOODDDDD

The high bit of the VxD ID is reserved for future use. The next 10 bits are
the OEM number, which should be requested from Microsoft. The last 5 bits
are the VxD number. This enables each OEM to create 32 unique VxDs. If an
OEM is creating a replacement for a standard VxD, then it should re-use the
standard ID. VxDs that do not provide services or APIs or that do INT 2FH
callouts should use the equate Undefined_Device_ID. See VMM.INC for other
examples of device IDs. Microsoft has reserved the first 16 OEM numbers (0
through 0FH).


16.4.5  VxD Initialization Order

VxDs are initialized in order from the lowest to the highest initialization
order value. If two or more VxDs have the same initialization order value,
then they are initialized in their order of occurence. Therefore, a specific
order is not guaranteed. You should use the initialization order equates in
VMM.INC to specify the initialization order for your VxD. If you want your
VxD to be initialized slightly before or after a defined initialization
order, use the equate plus an increment or minus a decrement.

Most likely your VxD will not have any initialization ordering dependencies.
In this case use the Undefined_Init_Order equate.


16.5  How VxDs Work

The following sections contain general explanations of how VxDs work and
provide information on the following topics:


  ■   Scheduling

  ■   Memory use

  ■   Services

  ■   Callback procedures

  ■   I/O port hooks

  ■   Interrupt hooks

  ■   Loading


The following related topics are covered in Appendix D, "Windows INT 2FH
API:"


  ■   Getting the address of a VxD API procedure

  ■   How a VxD calls out to a TSR or an MS-DOS device driver

  ■   How a TSR or MS-DOS device driver specifies a VxD to load when
      starting enhanced Windows




16.5.1  Enhanced Windows Execution Scheduling

The following is a brief description of how events are scheduled and
processed. The concepts are also described graphically in Figure 16.6.


Events

The enhanced Windows VMM is a single-threaded, non-reentrant operating
system. Because it is non-reentrant, virtual devices that hook interrupts
must have some method of synchronizing their calls to the VMM. For this
reason, enhanced Windows uses the concept of event processing.

Event procedures are registered asynchronously and, then, called back just
before the VMM returns to the application. At this point, the event
procedure can use all the VMM services.

VxDs can also use event procedures to perform some action on a VM that is
not the current VM. Examples of this include restoring the display to a VM
when the display focus changes or simulating an interrupt into a VM the next
time the VM is scheduled.

There are two types of events: global and VM specific. Global events are
processed before returning to a virtual machine regardless of which VM is
about to run. VM-specific events are only processed when the specified
virtual machine is about to run.


Scheduler

When Windows is running in 386 enhanced mode, each application runs in it
own VM. Each VM can be given a share of the CPU time. To assign priority
among the VMs, the VMM has a Scheduler.

The Scheduler is the part of the VMM that determines which VM gets CPU time.
It is divided into two parts. At the lowest level, the Primary Scheduler
maintains execution priorities, and the VM with the highest priority is
allowed to run. VxDs will raise and lower the execution priorities to affect
task switching among the VMs. The second level of scheduling is handled by
the Time Slicer, which boosts a VM's execution priority for a given time
slice.

With the Primary Scheduler, there are specific values assigned to execution
priorities to accomplish task switching, without violating the need for some
sections of code to execute exclusively until completion. Additionally,
high-priority device events, such as interrupts that must be serviced in a
timely manner, will boost execution priorities of VMs that need to be
serviced. The VMM provides services and defines execution priorities to
handle these cases.

The enhanced Windows Time Slicer is the preemptive multitasking portion of
the Scheduler. It relies on time-slice priorities and flags to determine how
much CPU time should be allocated to various virtual machines.

Every VM has a foreground and a background time-slice priority. These should
be distinguished from a VM's execution priority. The VM with the largest
execution priority will run, preventing other VMs from executing. The VM
with the largest time-slice priority will run more often than other VMs but
it will not necessarily prevent other VMs from executing.


Transitions Into and Out of the VMM and VxDs

The enhanced Windows VMM uses the protection mechanism of the 80386 to force
privilege ring transitions, as shown in Figure 16.6, whenever an application
program issues a software interrupt or causes a protection fault. One
example is when a VM performs I/O to a hooked port. The exact mechanisms
used to make the transition into the VMM are not important to a virtual
device developer. It is almost never necessary to directly intercept a
processor fault or hardware interrupt. The only device that handles hardware
interrupts directly is the Virtual PIC (Programmable Interrupt Controller)
Device. Callback procedures have been provided to signal a calling routine
when a specific event occurs. (See Section 16.5.4, "Callback Procedures,"
for more information.)

Programmers familiar with the 80386 architecture may assume that, to hook an
interrupt, a virtual device will hook the protected-mode Interrupt
Descriptor Table (IDT) directly. However, this is not true for Windows in
386 enhanced mode. Services to hook interrupts at this level are provided by
the VMM.

────────────────────────────────────────────────────────────────────────────
WARNING

VxDs must never modify the actual IDT. To do so will cause enhanced Windows
to crash.
────────────────────────────────────────────────────────────────────────────

The sequence of events for entering the VMM from a virtual machine because
of an interrupt is as follows:


  1.  The VM performs an operation that generates a fault.

  2.  A ring transition occurs, and the appropriate IDT interrupt handler is
      called.

  3.  The VMM dispatches the interrupt to the appropriate handler by a CALL.


  4.  The protected-mode handler processes the fault and executes a near
      RET.

  5.  The VMM processes any outstanding events.

  6.  An IRET is executed that causes a ring transition back to the VM.


Notice that the VMM looks at the interrupt before any virtual devices and
immediately before returning to the virtual machine.

(This figure may be found in the printed book).


16.5.2  Memory Models

Windows in 386 enhanced mode makes use of the 80386's ability to run within
different memory models. Some devices may have initialization code that is
run in real mode. See Section 16.5.7, "Loading Sequence," for the loading
sequence description. After that code is run successfully, a transition is
made to protected mode (using selector:offset addressing) in which the VMM
is installed and begins executing. The VMM creates a separate VM that
consists of a V86-mode portion and an optional, protected-mode (PM) portion
for each application.

The VMM and all the enhanced Windows VxDs run in a 32-bit, flat-model
protected mode segment. This means that every VxD has complete access to 4
gigabytes of linear address space. A VxD can access any VM's memory at any
time.

Because enhanced Windows is flat model, virtual devices cannot change the
CS, DS, ES, or SS segment registers. These segment registers always contain
a selector that has a base of 0 and a limit of 4 gigabytes. Devices can use
the FS and GS segment registers, but there usually is no reason to do so.
VMM services will not modify the FS or GS segment registers. Pointers are
always 32-bit linear addresses unless otherwise specified.

────────────────────────────────────────────────────────────────────────────
NOTE

Since the VMM (privilege ring 0 code) resides in a single, flat memory
segment, the selector of the selector:offset PM addressing for the VMM and
VxDs never changes.

────────────────────────────────────────────────────────────────────────────


Modes

Application programs typically run in a V86-mode portion of the enhanced
Windows operating environment.

As described in Appendix B, "Understanding Modes," V86 mode is similar to
real mode. The crucial difference between the two is that memory protection,
virtual memory, and privilege-checking mechanisms are in effect when code
runs in V86 mode. Therefore, a program executing in V86 mode cannot
interfere with the operating environment or damage other processes. If the
program reads or writes memory addresses that have not been mapped into its
VM or manipulates I/O ports to which it has not been allowed access, an
exception (fault) is generated, and the operating environment regains
control.

An application may also run in protected mode (also described in Appendix
B). An example of this is the the Windows graphical interface.

However, non-Windows applications must use DPMI when using protected-mode
features. The DPMI (DOS Protected Mode Interface) specification is available
from Intel at 1-800-548-4725. By using DPMI, applications gain access to
extended memory, the ability to run in a 16-bit or a 32-bit memory model,
and other features of the 80386 protected mode.


Privilege Rings

By running in ring 0, the VMM and all the VxDs have the most privileges.
Protected-mode applications, such as Windows itself, run in rings 1, 2, or 3
and have lower privileges. Least privileged are MS-DOS and the applications
running in V86 mode, which run only in ring 3.

Figure 16.7 shows each of these components within there respective rings.
Also shown are the Control Block and the Client Register Structure.

Since all virtual devices run at ring 0, they have the ability to execute
any 80386 instruction without producing a protection violation. However,
devices should not execute protected instructions as they will usually cause
Windows to crash immediately. The only exception to this is the Virtual Math
Coprocessor Device, which is allowed to change the 80387 bits in the CR0
register.

(This figure may be found in the printed book).


16.5.3  Services

Services are the shared routines of the VMM and VxDs. VxDs use services to
handle interrupts, to initiate callback procedures, and to process
exceptions/faults.

Notice that there are some VxD services that the VMM requires. Most notable
of these are the services provided by the Virtual Programmable Interrupt
Controller Device (VPICD), which virtualizes the PIC for the VxDs (for
requesting interrupts) and the VMs.

Detailed descriptions of each service are provided in Part 4, "Virtual
Device Services." The services are also categorized there as follows:


  ■   Memory Management services

  ■   I/O services and macros

  ■   VM Interrupt and Call services

  ■   Nested Execution services

  ■   Break Point and Callback services

  ■   Primary Scheduler services

  ■   Time-Slice Scheduler services

  ■   Event services

  ■   Timing services

  ■   Processor Fault and Interrupt services

  ■   Information services

  ■   Initialization Information services

  ■   Linked List services

  ■   Error Condition services

  ■   Miscellaneous services

  ■   Shell services

  ■   Virtual Display Device (VDD) services

  ■   Virtual Keyboard Device (VKD) services

  ■   Virtual PIC Device (VPICD) services

  ■   Virtual Sound Device (VSD) services

  ■   Virtual Timer Device (VTD) services

  ■   V86 Mode Memory Manager Device services

  ■   Virtual DMA Device (VDMAD) services

  ■   Virtual DOSNET Device services



16.5.4  Callback Procedures

Some services enable a calling routine to register a procedure that will be
called back when a particular event occurs. Callback procedures are used for
maintaining the VM state via I/O and interrupt trapping, and for
synchronizing with the VMM via the event services.

The VMM includes services that enable virtual devices to install callback
procedures to do the following:


  ■   Trap interrupts from virtual machines

  ■   Trap I/O to specific ports

  ■   Trap access to memory

  ■   Schedule per-VM or global time-outs

  ■   Schedule per-VM or global events

  ■   Detect when a VM returns from an interrupt or FAR call

  ■   Detect when a VM executes a particular piece of V86 code

  ■   Detect the release of the critical section

  ■   Detect changes to the VM's interrupt enable flag

  ■   Detect task switches



16.5.5  I/O Port Hooks

The VMM provides a service called Install_IO_Handler. The service takes two
parameters: the port to be hooked, and the address of the procedure to be
called whenever the port is accessed.

When a VxD calls Install_IO_Handler, the VMM sets the appropriate bit in the
I/O permission map (IOPM) and registers the procedure. When a virtual
machine executes an instruction that reads or writes data from an I/O port,
the 80386 looks up the port number in the I/O permission map. If the
corresponding bit in the IOPM is set, then the instruction will cause a
protection fault that results in calling the registered procedure.


16.5.6  Interrupt Hooks

The Virtual Programmable Interrupt Controller Device (VPICD) routes hardware
interrupts to other virtual devices, provides services that enable virtual
devices to request interrupts, and simulates hardware interrupts into
virtual machines.

When a virtual device needs to hook a specific IRQ, it must ask VPICD for
permission. If another device has already virtualized the IRQ, then VPICD
will refuse. Chapter 37, "Virtual PIC Device (VPICD) Services," details the
workings of the VPICD.

For services used in hooking software interrupts and supporting software
interrupt hooks, see Chapter 21, "VM Interrupt and Call Services."


16.5.7  Loading Sequence

The following is a generalized description of the loading sequence. Figures
16.8 and 16.9 are an example of a specific loading sequence.

When Windows in 386 enhanced mode is first started, the following happens:


  1.  The loader allocates extended memory via the XMS driver, loads the VMM
      and all the specified virtual devices into extended memory.

  2.  The system switches to protected mode.

  3.  The loader passes control to the VMM initialization routine.

  4.  The initialization routine completes the initialization of the VMM and
      calls all the VxD initialization routines.

  5.  The System VM is created and initialized.

  6.  The Shell VxD executes WINSTART.BAT and, then, Windows.


Each VxD can have different sections of code that are executed during
various phases of initialization and normal program execution, as shown in
Figure 16.8.

The first phase of initialization is load time. During load time, the
virtual device can abort the loading of the device, abort the loading of
enhanced Windows, specify instance data, and exclude pages of memory from
utilization by enhanced Windows. This load time code is in its own segment
and is run in real mode and, then, discarded. See Chapter 17, "Virtual
Device Programming Topics," for details on real-mode initialization.

The rest of the virtual device is run in 32-bit, flat-model protected mode
and is divided into the following four parts:


  ■   Initialization code

  ■   Initialization data

  ■   Code

  ■   Data


The initialization code and data are purged from memory after
initialization, as shown in Figure 16.9. These segments contain services and
data that are accessed only during the three phases of enhanced Windows
system initialization: Sys_Critical_Init, Device_Init, and Init_Complete.
Some of the enhanced Windows VMM services are available only during
initialization.

The sections of code and data that are not specifically for initialization
perform the device virtualization and can provide services for other
devices.

(This figure may be found in the printed book).

(This figure may be found in the printed book).


16.6  VxD Examples

Often, new VxDs are actually modifications of existing ones. To help with
your VxD development, Microsoft includes with the DDK the source code for
fully operational VxDs. We encourage you to use them as examples whenever
convenient. See the DDK  Installation and Update Guide for more detailed
descriptions and a complete list of the source samples provided for this
version of Windows.






Chapter 17  Virtual Device Programming Topics
────────────────────────────────────────────────────────────────────────────

This chapter presents details on writing and installing VxDs. You should be
familiar with Chapter 16, "Overview of Windows in 386 Enhanced Mode," before
proceeding with this material. For explanations on specific types of
services provided by the Virtual Machine Manager (VMM), see the appropriate
chapters in Part 4, "Virtual Device Services." This chapter is divided into
the following general topics:


  ■   Writing VxDs

  ■   Building a VxD

  ■   Initializing a VxD

  ■   Tracking the VM states

  ■   Exiting Windows


We recommend that you scan all the topics before beginning a VxD project.
You should also review the sample VxDs supplied on the Microsoft Windows
Device Development Kit (DDK) disks for examples of how to accomplish
specific tasks. The following table suggests some VxDs to study when
investigating specific service topics.

╓┌─────────────────────────────────┌─────────────────────────────────────────╖
Service topic                     Sample VxD
────────────────────────────────────────────────────────────────────────────
Memory management                 VDD
Hardware interrupts               VKD
I/O                               VPD
Service topic                     Sample VxD
────────────────────────────────────────────────────────────────────────────
I/O                               VPD
Scheduler                         VKD
Events                            VKD
Timeouts                          VKD



17.1  Writing VxDs

Enhanced Windows virtual devices are not "Windows" programs. You do not need
to know anything about Windows programming to write a VxD.

Often, new VxDs are simply modifications of existing ones. To help with your
VxD development, Microsoft includes the code for many, fully operational
VxDs in the Microsoft Windows Device Development Kit. We encourage you to
use them as examples whenever convenient.

However, some VxDs will require a significant effort to develop. The
following can be used as a guideline when writing a complex VxD.


  1.  Build a skeleton. Using the supplied sources as a guide, build a
      skeleton of the VxD with the device control procedure, the services,
      and the API procedures defined but not functional.

  2.  Add the initialization functionality, including the control block and
      global memory allocation, physical page hooking, I/O hooking, and
      interrupt hooking.

  3.  Fill out the procedures that handle the various hooks.

  4.  Test the procedures.

  5.  Implement the APIs and services, if there are any.

  6.  Test the APIs and services.




17.1.1  VxD Memory Model

The part of the enhanced Windows environment containing the VMM and all the
VxDs (ring 0), is one, flat-model, 32-bit segment. This means that all the
code and data belong to the same group. Two selectors are created: one for
code and one for data. Both have a base of zero and a limit of four
gigabytes, so all the segment registers point to the same address space (the
entire virtual address space provided by the 80386 processor).

When a VxD is loaded, all the offsets are fixed according to the the VxD's
actual position. This is different from MS-DOS's loading of .EXE files, in
which segments are fixed up and offsets are left untouched.

All procedures are NEAR, and data pointers are 32-bit offsets.

VxDs do not export routines or data. To access VMM or VxD services, a
dynamic-link mechanism is employed using macros contained in VMM.INC. The
VMM services are available with the VMMcall macro, and the VxD services with
the VxDcall macro. Data is shared via declared services only.

You must use the OFFSET32 macro in your flat-model, 32-bit segments anywhere
you would normally use the OFFSET assembler directive. That is, in all
segments except for the real-mode initialization segment. This macro
correctly defines all the offsets so that LINK386 will produce the correct
offset fixup information. For example:

  mov   esi, OFFSET32 My_Data


17.1.2  VxD Segmentation

As discussed in Chapter 16, "Overview of Windows in 386 Enhanced Mode," VxDs
have five functional parts. Each of these parts exists as a separate
segment. Macros have been created to define segments for each of the parts.

Each macro name consists of a segment name followed by "_SEG," which means
that this macro begins the segment. A segment descriptor terminated by
"_ENDS" is used for macros that end the segment. For example, macros used
for defining a segment for real-mode load-time initialization would appear
as VxD_REAL_INIT_SEG and VxD_REAL_INIT_ENDS.

In some enhanced Windows installations, it is possible to demand page
portions of VxDs. These installations require a dedicated swap device or a
fully virtualized hard disk with a dedicated swap partition. This way,
paging can be done without concern for reentering portions of MS-DOS, device
drivers, or BIOS. To support paging, a VxD must place the following in
locked memory:


  ■   Device Control Procedure (DCP)

  ■   Device Descriptor Block (DDB)

  ■   Hardware interrupt procedures (and the data accessed by them)

  ■   Asynchronous (Async) services that can be called from hardware
      interrupt procedures


Some of the macros supplied in VMM.INC (e.g., Declare_Virtual_Device)
correctly place code and data objects in locked segments. The following are
the different segment descriptor types:

  VxD_REAL_INIT  - Real-mode load-time initialization
  VxD_ICODE  - Protected mode initialization code
  VxD_IDATA  - Protected mode initialization data
  VxD_LOCKED_CODE  - Code that cannot be paged
  VxD_LOCKED_DATA  - Data that cannot be paged
  VxD_CODE   - Pageable code
  VxD_DATA   - Pageable data


17.1.3  VxD Declaration

A VxD's first few lines of code must always be the assembler directive
(".386p"), the INCLUDE files, and the Device Descriptor Block declaration.


Assembler Directive

Every VxD must inform the assembler that the code is 80386 protected-mode
code. This is done by including the following directive:

  .386p


INCLUDE Files

INCLUDE files enable VxDs to use code located in other parts of enhanced
Windows. The following INCLUDE files may be included:

╓┌─────────────────────────────────┌─────────────────────────────────────────►
Filename                          Description
────────────────────────────────────────────────────────────────────────────
VMM.INC                           (Required) Contains definitions of all the
                                  enhanced Window services, as well as
                                  required macros and equates.

DEBUG.INC                         (Optional) Contains useful macros for
                                  dumping messages to a
                                  debugging terminal and performing checks
                                  on various data. The macros provided by
                                  this file produce code only when the VxD
                                  is assembled with the DEBUG switch. See
                                  the Microsoft Windows Software Development
Filename                          Description
────────────────────────────────────────────────────────────────────────────
                                  the Microsoft Windows Software Development
                                  Kit (SDK) for information on the Windows
                                  debugging services.

VPICD.INC                         (Optional) Contains equates and service
                                  declarations for the
                                  Virtual Programmable Interrupt Controller
                                  Device (VPICD). All enhanced Windows
                                  interrupts are handled by the VPICD. Most
                                  VxDs will require VPICD services. For
                                  example, the VPD uses the services to hook
                                  all the printer port's hardware interrupts.

SHELL.INC                         (Optional) Contains definitions of the
                                  public services provided by the Shell VxD.
                                  The Shell device provides the VxDs with
                                  access to the Windows graphical user
                                  interface, thus giving the VxDs the
                                  ability to display dialog boxes to the
Filename                          Description
────────────────────────────────────────────────────────────────────────────
                                  ability to display dialog boxes to the
                                  user. For example, if two VMs attempted
                                  simultaneously to use the same peripheral
                                  device, the VxD could call
                                  Resolve_Contention, which would display a
                                  dialog box asking the user to choose
                                  between the two VM applications.




Device Descriptor Block

The declaration of the VxD is accomplished by its Device Descriptor Block
(DDB). The DDB is generated automatically by the Declare_Virtual_Device
macro. The following example is from the VPD sample provided with the DDK.

  DECLARE_VIRTUAL_DEVICE VPD,3,0,VPD_Control, VPD_Device_ID,
VPD_Init_Order,, VPD_PM_API_Handler,,

The table in Figure 17.1 describes each of the parameters:

(This figure may be found in the printed book).


VxD IDs

To ensure cooperation with current and future versions of enhanced Windows
environment components, VxDs need to have unique ID numbers assigned. If you
are directly replacing an existing VxD, use the previous ID. Otherwise, if
you are creating a new VxD that provides any of the following:


  ■   Service(s)

  ■   V86 mode API (see Appendix D, Section D.1.6, "Get Device API Entry
      Point")

  ■   Protected Mode API (see Appendix D, Section D.2.3, "Device Call Out
      API")


or if the VxD needs to call an MS-DOS device (listed in .SYS) or TSR, the
VxD will need a new ID. Device IDs are a 16-bit combination of the OEM
number and the device number in the form:

  xOOOOOOOOOODDDDD

The high bit of the VxD ID is reserved for future use. The next ten bits are
the OEM number that is assigned by Microsoft. The last five bits are the
device number. This enables each OEM to create 32 unique VxDs. VxDs that do
not provide services or APIs or that do INT 2FH call-outs should use the
equate Undefined_Device_ID.

See VMM.INC for other examples of device IDs. Microsoft reserves the first
16 OEM numbers (0 through 0FH).


Initialization Order

VxDs are initialized in order from the lowest to the highest initialization
order value. If two or more devices have the same initialization order
value, then they are initialized in their order of occurence. Therefore, a
specific order is not guaranteed. You should use the initialization order
equates in VMM.INC to specify the initialization order for your VxD. If you
want your VxD to be initialized slightly before or after a defined
initialization order, use the equate plus an increment or minus a decrement.
Most likely, your VxD will not have any initialization ordering
dependencies. In this case, use the Undefined_Init_Order equate.


17.1.4  VxD Services

The functionality a VxD provides, if either directly to the VMM or other
VxDs, or through them to applications, is always via services. The services
use a dynamic-linking mechanism using INT 20H. (This mechanism is not the
same as the DLL protocol.) An example of a service call is shown below.

This section contains descriptions on how to declare and call services, how
to verify the presence of a VxD, and a comparison of standard vs.
asynchronous services.


Service Calling Conventions

All the enhanced Windows services use either a register-based calling
convention or a 32-bit C-type calling convention. In general, all the memory
manager calls use C calling conventions, and all other services are register
based.

The C convention services all begin with an underscore (_) in front of the
service name. They are similar to the standard C conventions: all parameters
are passed on the stack, and results are returned in the EAX and EDX
registers.

Unlike the standard C conventions, the EBX, ES, FS, and GS registers are
preserved as well as the ESI and EDI registers. Only the flags and the EAX,
ECX, and EDX registers are modified.

The VMMcall and VxDcall macros support stack parameter passing like the
standard C macro package. For example:

  VMMcall _HeapAllocate, <<SIZE Data_Node>, 0>>

will generate the following code:

  push 0
  push SIZE Data_Node
  int 20h
  dd _HeapAllocate
  add esp, 2*4

Notice that the parameters are pushed on the stack from right to left as in
the standard convention.

All the Windows services for running in 386 enhanced mode that do not begin
with an underscore (_) are register-based services. All the parameters to
the services are passed in registers and all the results are returned in
registers. If a service does not explicitly return a result in a register,
than that register will be preserved.


Declaring Services

Virtual devices use two macros, Begin_Service_Table and End_Service_Table,
that are declared in VMM.INC to export services. The service table is
normally declared in an INCLUDE file that other VxDs can include to import
the services. For example, a typical service table declaration would look
something like this for the Virtual "FOO" Device:

  Begin_Service_Table VFooD

  VFooD_Service vFooD_Get_Version, LOCAL
   VFooD_Service vFooD_Do_Something
   VFooD_Service vFooD_Do_Somthing_Else
   VFooD_Service vFooD_Do_Yet_Another_Thing, VxD_ICODE

  End_Service_Table VFooD

The Begin_Service_Table macro uses a single argument to generate the macro
used to declare individual services. Begin_Service_Table names the macro by
taking the name of the device and appending "_Service" to it. In the
preceding example, VFooD_Service is the name of the macro.

The Device_Service macro can take one or two parameters. The first parameter
is the name of the service (e.g., Get_Version). This must match the name of
a procedure that was declared with the BeginProc macro using the "Service"
or "Async_Service" options. The second parameter is optional. If it is
omitted, then the service procedure is declared as an external reference in
the VxD_CODE segment.

If the special value "LOCAL" is used as the second parameter (as in the
VFooD_Get_Version declaration), then the procedure is not declared as
external. This option is used when the service is declared in the same file
in which the service table will be created. If, in this case, it were to be
declared external, then MASM would generate an error.

If the service procedure is not in the same file as the one used to create
the service table, and not in the VxD_CODE segment, then you must supply the
name of the segment it resides in so that the proper external declaration
can be made. In the above example, the VFooD_Do_Yet_Another_Thing service is
declared to be in the VxD_INIT code segment. All others are left in the
default VxD_CODE segment.

The first service for every device must be a Get_Version service. This
service must return with AX  0 and the Carry flag clear. See the following
section, "VxD Presence Verification," for more details.

Once the table of services has been created, you must force the table to be
generated in one of the VxD source files by defining a special equate (EQU)
called "Create_xxx_Service_Table," where xxx is the name of the device
before including the service declaration INCLUDE file. For example, the main
source file of the VFooD service table would contain the following INCLUDE
statements:

  INCLUDE VMM.INC
  INCLUDE Debug.INC
  Create_VFooD_Service_Table EQU true
  INCLUDE VFooD.INC

This must be done in the same source file that contains the device
declaration. This table is automatically generated and the pointer to the
table is stored in the device's DDB.

Notice that, since the macros generate equates, you will now want to add
service declarations to the end of the INCLUDE file. That is, never change
the order of the declarations. Adding, removing, or changing the order of
services changes the service numbering and all the VxDs that call these
services will need to be rebuilt.


VxD Presence Verification

Many VxDs will not load under certain circumstances. For example the EBIOS
device will not load when the machine does not have an extended BIOS data
area. Before calling VxD services, you should make sure that the VxD is
loaded by calling the VxD Get_Version service. Get_Version for a VxD will
return with AX = 0 and the Carry flag set if the VxD is not installed.


Standard Vs. Asynchronous Services

Most services are not reentrant. This means they cannot be called from
hardware interrupt procedures. However, a select group of services is
declared as "Async" services and can be called from hardware interrupt
procedures. You may declare services that can be called from interrupt
handlers by using the "Async_Service" option for the BeginProc macro.

When writing async services, do not call non-async services, and be sure the
routine can handle reentrancy.


17.1.5  VxD APIs (Call-Ins)

While VxD services are used to communicate with other enhanced Windows
virtual devices, APIs are used to communicate with software running in a
virtual machine. For example, the Shell device supports an API that is used
to communicate with WinOldAp (the Windows support program for non-Windows
applications) that runs in the System VM.

A VxD can support API for V86-mode code, protected-mode code, or both. The
procedure entry point(s) for the API is specified in the device declaration
macro (see Section 17.1.3, "VxD Declaration," for more details on
Declare_Virtual_Device). The VM software issues an INT 2FH with AX = 1684H
and BX = Device_ID to get the address to call to access the API. See
Appendix D, "Windows INT 2FH API," for more information.

The application can then call this address, passing appropriate information
in registers. The same address is used to call all API functions provided by
a single VxD.

When such a call is made, the VMM will in turn call the VxD's API procedure
with the following parameters:

  EBX = Current VM handle
  EBP = Client register structure
  Client_CS:IP = Instruction following API call

API procedures must examine the client registers (through the client
register structure) to determine which API call was made. The normal calling
convention uses AH = Major function number and AL = Minor function number.
Other registers are used for parameters to the functions. However, a device
can use any calling convention that is appropriate. If you want to return a
value to the caller, then the API procedure should modify the client
registers.

API procedures may modify the EAX, EBX, ECX, EDX, ESI, and EDI registers.


17.1.6  VxD INT 2F Usage

Appendix E, "The Windows INT 2F API," includes several INT 2F functions that
are of interest to VxD writers. Specifically, there is an INT 2F to get the
address for calling the VxD API procedure, an INT 2F for a VxD to call out
to a T&SR or a DOS device driver, and an INT 2F by which a T&SR or DOS
device driver can specify a VxD to load when starting enhanced mode Windows.
If your VxD interacts with global software, be sure to study this appendix.



17.2  Building a VxD

This section describes in general the steps necessary to build and install a
VxD into the enhanced Windows environment. These steps are typically
specified and executed from the MAKE file. Detailed examples are also
included in a MAKE files located on the supplied DDK disks.

There are three required steps for building a VxD, with each requiring a
specific software tool:


  1.  Assemble the VxD code with MASM5.10B, which is the special version of
      the assembler used to handle a new pseudo group, FLAT. (.ASM  .OBJ)

  2.  Link the .OBJ files utilizing a .DEF file, with LINK386. LINK386 is
      the linker used to create the special 32-bit .EXEs.(.OBJ  .386, .MAP)

  3.  Declare the code to be a VxD with ADDHDR, which adds special VxD
      information into the .EXE produced with LINK386.(.386  .386)

      An optional fourth step, is available for debugging:

  4.  Generate symbol files with MAPSYM32, which generates 32-bit symbol
      (.SYM) files for debugging.(.MAP  .SYM)


These four tools are included in this version of the DDK. See the following
sections for detailed invocation instructions.

The following MAKE file sample is from the Virtual Printer Device (VPD). The
complete source for building the VPD is included on the DDK disks.

  vpd.obj: vpd.asm
           masm5 -p -w2 vpd;

  vpd.386: vpd.obj vpd.def
           link386 vpd, vpd.386/NOI /NOD /NOP,/MAP,,vpd.def
           addhdr vpd.386
           mapsym32 vpd

The MAKE file assumes that the four tools are included in the MS-DOS PATH
command. If they are not, then you must modify the MAKE file to specify
their exact locations.


17.2.1  MASM5.10B

This is a special version of MASM that supports 32-bit, flat-model code. It
has been named MASM5 to differentiate it from other versions of MASM you may
already have. It has the same command-line options and format as MASM 5.1,
so you can refer to version 5.1 documentation for information on this
program.

It is recommended that the -p and -w2 options be used when assembling
virtual devices. The -p option specifies that impure code segment references
should generate warning messages. This is desirable, because it is illegal
to write data with a CS override. The -w2 option sets the warning level to
2, so that warning messages are generated for such things as jumps that are
within SHORT range and for data size mismatches.

MASM5 will look for INCLUDE files in the current directory and the INCLUDE
path specified by the environment variable INCLUDE. Therefore, the DDK
INCLUDE files (e.g., VMM.INC, VPICD.INC, and VDD.INC) should be either in
the current directory or located along the INCLUDE path.


17.2.2  LINK386

The LINK386 command line is as follows:

  LINK386 <object file>, <output file> {<options>}, <map file>, <def file>

For example:

  link386 vpd, vpd.386/NOI /NOD /NOP, /MAP,,vpd.def

LINK386 links into one device file the individual object (OBJ) files that
make up a virtual device. By convention, Windows devices have the extension
.386. The command line specifies the object files(s), the desired output
file, option switches, and definition file. The following list describes the
option switches used to link a VxD.

╓┌───────────┌───────────────────────┌───────────────────────────────────────╖
Option      Full Name               Description
────────────────────────────────────────────────────────────────────────────
/NOI        NOIGNORECASE            Specifies that case should not be
                                    ignored.

/NOD        NODEFAULTLIBRARYSEARCH  Specifies that LINK386 should not
                                    search for default libraries.

/NOP        NOPACKEDCODE            Specifies that code segments should
                                    not be packed into one code segment in
                                    the .EXE file.

Option      Full Name               Description
────────────────────────────────────────────────────────────────────────────

/MAP                                Specifies that all public symbols
                                    should be included in the MAP file.

────────────────────────────────────────────────────────────────────────────



Definition (DEF) files are used by LINK386 to identify the device descriptor
block within the device and the types of segments. DEF files for virtual
devices all look similar to the following example:

  LIBRARY VPD

  DESCRIPTION 'Win386 VPD Device (Version 2.0)'

  EXETYPE DEV386

  SEGMENTS
   _LTEXT PRELOAD NONDISCARDABLE
   _LDATA PRELOAD NONDISCARDABLE
   _ITEXT CLASS 'ICODE' DISCARDABLE
   _IDATA CLASS 'ICODE' DISCARDABLE
   _TEXT CLASS 'PCODE'   NONDISCARDABLE
   _DATA CLASS 'PCODE'   NONDISCARDABLE

  EXPORTS
   VPD_DDB @1

The LIBRARY line is required to identify the device as a module that is part
of a system rather than an executable application. The DESCRIPTION line is
optional and simply records the text string into the .386 file. The EXETYPE
line is required to identify the .386 file as an enhanced Windows device
file.

The SEGMENTS section is identical for all devices, because it identifies the
six possible types of protected-mode segments that can be part of a device.
(If a device has a real-mode initialization section, then it can have seven
types of segments. However, the real-mode section does not need any special
identification in the DEF file.)

The EXPORTS section is also required; it identifies the name and location of
the device descriptor block for the virtual device. It must match the name
used in the Declare_Virtual_Device statement in the device source, with _DDB
appended to the end. It must also be identified as ordinal number 1 with the
@ symbol.


17.2.3  ADDHDR

The ADDHDR command line is as follows:

  addhdr <filename>

For example:

  addhdr vpd.386

ADDHDR simply reads the specified 32-bit .EXE file, performs some validation
checks, and writes some additional header information needed by the enhanced
Windows loader into the file's .EXE header.


17.2.4  MAPSYM32

The MAPSYM32 command line is as follows:

  mapsym32 <device name>

For example:

  mapsym32 vpd

MAPSYM32 reads a MAP file and creates a 32-bit .SYM file for use with the
Windows debugger, WDEB386. The /MAP option must be specified for LINK386 to
generate the necessary MAP file.


17.2.5  Installing a VxD

In order to install the newly created VxD with the rest of enhanced Windows,
edit the SYSTEM.INI file. in the [386ENH] section by inserting DEVICE =
<your VxD file name> and put your file in the Windows SYSTEM subdirectory.


17.3  Initializing a VxD

As described in Chapter 16, "Overview of Windows in 386 Enhanced Mode," VxDs
are initialized along with the enhanced Windows environment. Both real-mode
and protected-mode code may be used and are described in the following
subsections.


17.3.1  Real-Mode Initialization

Each VxD can have a portion that is run in real mode at load time. This
capability is provided to enable a VxD to determine whether or not it can
operate in the current environment and to provide information to the loader
about how it should vary the environment. This portion is only executed at
load time and, then is discarded.

A real-mode portion is included in the same binary file as the rest of the
VxD code and data, but is located in a special 16-bit sement. It has a
single entry point, declared as a NEAR procedure. At load time, if the
loader determines that a real-mode portion is present, it loads it and calls
its entry point as specified by the END statement at the end of the file
(CS:0 if no entry point is found). Upon entry CS = DS = ES, so code and data
must be mixed in the same segment. The code can then perform the checks that
are necessary and return an exit code back to the loader.

Valid exit codes are as follows (see VMM.INC for equates):


  ■   0 - Everything is fine, and the loader should continue loading the
      protected-mode portion and the rest of the VxDs.

  ■   1 - This device is not compatible with the current environment and
      will not be loaded, but the loader can continue to load other VxDs.

  ■   2 - Something is wrong and the loader should abort the Windows load
      completely.


If 1 or 2 is returned, then the loader will normally print an appropriate
error message naming the VxD that failed. If the real-mode portion has
already handled the message reporting or does not want any default error
message, then it should set the high bit of the return code in AX (i.e.,
8001H or 8002H.)

The real-mode portion can also inform the loader to do the following:


  ■   Pass a DWORD of reference data to the protected-mode portion of the
      device.

  ■   Pass a table of pages in low memory (0-1Mb) that should be excluded
      from general use by the enhanced Windows memory manager.

  ■   Pass a table of pointers to data that should be instanced for each
      virtual machine.


It is possible for a VxD to exclude pages and/or declare instance data
without actually having a protected-mode portion; it should return 8001H as
the return code, so that the loader will attempt no further loading of the
device and will not display the default error message.

The real-mode portion can perform most BIOS or MS-DOS interrupts and examine
memory to check the environment of the machine. It cannot attempt to perform
any type of MS-DOS exit calls because these will halt the loader in an
unclean state, and it will be necessary to reboot the machine. Also, any
open files should be closed before returning since they will not be closed
by the loader.

The following is a summary of entry assumption:

CS = DS = ES = segment of loaded code and data IP = specified entry point or
0. SI = environment segment, passed to the loader from MS-DOS AX = VMM
version number BX = flags    bit 0: duplicate device ID already loaded
bit 1: duplicate ID was from the INT 2F device list    bit 2: this device is
from the INT 2F device list EDX = reference data from INT 2F response, or 0


SS:SP point to loader's stack.

The following is a summary of exit assumptions:

Must return with a NEAR return AX = return code (see above) BX = ptr to list
of pages to exclude (0, if none), where:    list is = one or more words in
the range 1 to 9FH (terminated) by a word of zero SI = ptr to list of
Instance data items (0, if none), where:     list is = one or more instance
data items followed by three words   of zero (note that 0-3FF, the interrupt
vectors are   always instanced). instance data item = pointer to   data
(word segment, word offset),word length of data

EDX = DWORD of reference data to be passed to the protected-mode portion.
(This can be a linear pointer to ROM data, a constant, etc. that will affect
the way the protected portion might operate. For example, an EBIOS device
can pass the EBIOS page number, so that the protected-mode portion does not
have to look for the page again.)

All the other registers except SS:SP can be modified.

The macros VxD_REAL_INIT_SEG and VxD_REAL_INIT_ENDS are defined in VMM.INC
to facilitate creating a real-mode portion of a device driver. The real-mode
portion cannot access code or data outside of its segment. If this is
attempted, the linker will generate warnings and a corrupt .386 file. Fixed
segments such as the BIOS (40H) segments are an exception to this. It is
possible to have declared in multiple source files real-mode portions that
will all be linked together (e.g., separating message text from the code).

The following is an example of real-mode initialization code:

  VxD_REAL_INIT_SEG
  BeginProc ebios_init
   mov ah, 0C0h
   int 15h
   test es:[bx.SD_feature1], EBIOS_allocated
   jz short no_ebios_fnd
   mov ah, 0C1h      ; get segment adr of EBIOS
   int 15h

   jc short no_ebios_fnd
   mov ax, es    ; get EBIOS segment address
   shr ax, 8    ; convert to a page #
   movzx edx, ax     ; return EBIOS pg as ref
        ; data
   mov bx, OFFSET exc_ebios_page  ; ptr to exclusion table
   mov [bx], ax    ; exclude EBIOS page
        ; from memory manager use
   xor si, si    ; no instance data to
        ; declare
   mov ax, Device_Load_Ok  ; go ahead and load the
        ; device
   jmp short init_exit   ; return to loader

  no_ebios_fnd:
   mov ah, 9
   mov dx, OFFSET no_ebios_msg    ; print message thru DOS
   int 21h
   xor bx, bx    ; no exclusion table
   xor si, si    ; no instance data table
   xor edx, edx    ; no reference data
   mov ax, Abort_Device_Load + No_Fail_Message
        ; don't load pmode portion
        ; and don't display an
        ; error msg

  init_exit:
   ret
  exc_ebios_page dw 0, 0
  no_ebios_msg db 'PS/2 type EBIOS not detected', 13, 10, '$'
  EndProc ebios_init
  VxD_REAL_INIT_ENDS
  END ebios_init     ; specify real mode
        ; initialization entry point


17.3.2  Protected-Mode Initialization

The enhanced Windows environment has a three-phase, protected-mode
initialization. Returning a carry during any of the phases will abort the
VxD load.


Phase 1. Sys_Critical_Init

During the first phase of initialization, interrupts are not yet enabled.
Therefore, this phase should accomplish the following tasks as quickly as
possible.


  ■   Initialization of critical functions necessary when interrupts are
      enabled.

  ■   Claiming a particular range of V86 pages if necessary (such as the
      video memory for the VDD).

  ■   Initialization of data needed by the services provided by the VxD.

  ■   During this phase, the System VM Simulate_Int and Exec_Int commands
      must not be used.



Phase 2. Device_Init

During this phase most VxDs do the bulk of their initialization. They will:



  ■   Allocate their Control Block area

  ■   Allocate other required memory areas

  ■   Hook interrupts

  ■   Hook I/O ports

  ■   Specify instance data

  ■   Initialize themselves


The System VM's Control Block should be set up with the inital state of the
VxD. Since the System VM has already been created, calls such as
Simulate_Int or Exec_Int is allowed.


Phase 3. Init_Complete

This is the final phase of Device_Init that is called just before the WIN386
INIT pages are released and the instance snapshot is taken. VxDs that want
to search for a region of V86 memory to use should do so during this phase.
Most devices, though, will not need to do anything here.


17.4  Tracking the VM States

Most likely, the VxD that you are writing needs to keep track of the status
of the different VMs that may need your VxD. This includes VM creation,
initialization, and termination. The following subsections describe these
and other possible VM states.


17.4.1  VM Creation and Initialization

Like the initialization of the enhanced Windows environment, a VM's go
through a multi-phase initialization process.


Phase 1. Create_VM

This call creates a new VM. EBX = VM handle of the new VM. Returning Carry
will fail the Create_VM. VxDs should initialize data associated with the VM,
especially the control block.


Phase 2. VM_Critical_Init

EBX = VM handle of the new VM. Returning Carry will cause the VM to
VM_Not_Executeable, then be destroyed (see Section 17.4.3). VM Simulate_Int
or Exec_Int activity is not allowed.


Phase 3. VM_Init and Sys_VM_Init

The VxD interacts with the VM in order to initialize the state of the
software in the VM (e.g., the VDD does an INT 10H to set the initial display
mode.

System VM initialization is the same as other VM initializations except its
Phase 1 and 2 are done at Device_Init and if Carry is returned, all of
enhanced Windows will exit.


17.4.2  VM State Changes

During the normal execution of enhanced Windows, VMs will go through state
changes. Most state changes may be ignored by VxDs. However, depending on
the purpose of the VxD, some may require VxD response. The following calls
describe the possible VM state changes.


VM_Suspend

The VM is not runnable until a resume. EBX = VM handle. The call cannot be
failed. The VxD should unlock any resources associated with the VM.

The VM_Suspend message is sent only once each time the VM is suspended. Note
that there is a bit the the control block CB_VM_Status flags indicating the
current state. See VMM.INC for details.


VM_Resume

The VM is leaving a suspended state. EBX = VM handle. Returning a carry
fails and backs out of the resume. Lock any resources and otherwise prepare
internal data structures for the VM to start running again.

The VM_Resume message is sent only once after a VM_Suspend message has been
sent.


Set_Device_Focus

This sets the focus of the specified VxD to the specified VM. EBX = VM
handle of desired VM. EDX = VxD ID. For VxD specific set focus, EDX = 0 for
device critical set focus (all devices).

This call cannot be failed. Restore the hardware associated with the device
to the state of the specified VM. As much as possible, remove VxD
interaction with VM (such as disabling I/O trapping) so that VM can run as
fast as possible.


Begin_Message_Mode

This call prepares the device for message processing. This is only of
interest to the keyboard, mouse, and display. When in message mode, special
services provided by the display and keyboard are used to interact with the
user. Message mode is used for the ALT+TAB screen and for message boxes when
Windows is not available to process a message box. EBX = VM handle going
into message mode. This call cannot be failed.


End_Message_Mode

EBX = VM handle leaving message mode. This call cannot be failed.


Reboot_Processor

This call requests a machine reboot. The device (usually the keyboard
device) that knows how to reboot the machine does the necessary operations.



Query_Destroy

This call asks if it can destroy the running VM. Query_Destroy is an
information call made by the Shell device before an attempt is made to
initiate a destroy VM sequence on a running VM that has not exited normally.
EBX = VM handle. Returning Carry indicates that a VxD has a problem with
allowing this. It is recommended that the VxD returning the Carry indicating
a problem call SHELL_Message to post an informational dialog about the
reason for the problem.


Debug_Query

Debug_Query is a special call for device-specific DEBUG information display
and activity. This call is made in response to the user typing a period
followed by the VxD name in response to the WDEB386 prompt. The VxD name is
declared with the Declare_Virtual_Device macro (see Section 17.1.3, "VxD
Declaration"). Use conditional assembly to remove this call from the final
VxD.


17.4.3  VM Termination

Graceful termination of a VM occurs in the following three steps:


Phase 1. VM_Terminate

During this phase of normal VM termination. EBX = VM handle. Call cannot be
failed. VM Simulate_Int and Exec_Int activity is allowed.


Sys_VM_Terminate

Same as VM_Terminate, except terminates the System VM. System VM
Simulate_Int, Exec_Int activity is allowed. System VM will always be the
last VM terminated, and thereby indicates that enhanced Windows is
terminating. This call is only made during a normal exit, during a crash
exit this call is not made.


Phase 2. VM_Not_Executeable

During the second phase of VM termination. EBX = VM handle, EDX = Flags (see
VMM.INC). Notice that in the case of destroying a running VM, this is the
first call made (i.e., the VM_Terminate call does not occur). Call cannot be
failed. VM Simulate_Int and Exec_Int activity is not allowed. Flags for
VM_Not_Executeable control call (passed in EDX) are as follows:

╓┌─────────────────────┌─────────────────────────────────────────────────────╖
Flag                  Meaning
────────────────────────────────────────────────────────────────────────────
VNE_Crashed           VM has crashed.
VNE_Nuked             VM was destroyed while active.
VNE_CreateFail        Some device failed Create_VM.
VNE_CrInitFail        Some device failed VM_Critical_Init.
VNE_InitFail          Some device failed VM_Init.



Phase 3. Destroy_VM

During this final phase of VM termination. EBX = VM handle. Notice that
considerable time can elapse between the VM_Not_Executeable call and this
call. Call cannot be failed. VM Simulate_Int and Exec_Int activity is not
allowed.


17.5  Exiting Windows

There are two calls that can alert a VxD that enhanced Windows is exiting:
System_Exit and Sys_Critical_Exit. They both come after Sys_VM_Terminate, is
Windows is exiting normally.


System_Exit

This call is made when Windows is exiting either normally or via a crash.
Interrupts are enabled. System VM Simulate_Int and Exec_Int activity is not
allowed. However, the VxD may modify the System VM memory to restore the
system state to allow a graceful exiting of Windows.


Sys_Critical_Exit

This call is made when enhanced Windows is exiting either normally or via a
crash. Interrupts are disabled. Simulate_Int and Exec_Int activity is not
allowed. VxDs should reset their associated hardware to a quiescent state to
allow a graceful return to real mode.


17.6  Debugging a VxD

Their are a number of features built into the WDEB386 debugger and the
debugging version of the 386 enhanced mode environment to aid you in
debugging your VxD. These features require a terminal hooked up to a COM
port, as described in the WDEB386 documentation in the Microsoft SDK. When
making use of these features in your code, be sure to remove them from your
final product. See DEBUG.INC for useful macros and the sample source for
usage.


17.6.1  Entering WDEB386

There are four ways to initially enter the debugger:


  1.  Type CTL+ALT+SYSREQ.

  2.  Generate a non-maskable interrupt (this can be disabled with the "V2"
      command).

  3.  Place an "INT 1" instruction in your code. This method should be used
      when debugging the real-mode portion of your VxD. An "INT 3" may also
      be used but the debugger will not automatically pass over it.

  4.  Specify "\b" as a command line parameter to WDEB386. This will pass
      control to the debugger just prior to VMM initialization, after all
      VxDs have been loaded and the processor is running in protected mode.



17.6.2  Tracing Paths of Execution

You can dump strings of text and registers in hexadecimal to the debug
terminal utilizing the TRACE_OUT and DEBUG_OUT macros.

The Queue_out macro allows you to dump the same information to a trace
queue. You can also send VMM entry and exit information to the queue by
using the ".t" command, and send procedure logging information with the .VMM
command.

Trace_Out and Debug_Out strings have the following special characters
defined:

  #<register>, where
  <register> is a 32 bit register, a 16 bit register
      or AL, BL, CL or DL. This will insert the hex value for
      the indicated register into the string that is output at
      the location where #<register> appears.
  ?[<16 bit register>:]<32 bit register>, where the 16 bit register is
      a selector and the 32 bit register is an offset within
      that selector. If the 16 bit register is not specified,
      then 28h, the VxD code segment, is assumed. This will insert
      the symbol table name of the closest previous symbol
      corresponding to the address specified. If no symbol is
      found, nothing is printed (e.g. ?AX:EBX).

Additionally, a VxD can put data into the trace queue that is listed with
the .LQ command. To do this use the Queue_Out macro:

  Queue_Out "<string>"[,<reg1>[,<reg2>»

where reg1 and reg2 are 32-bit registers. Reg1 will be in the EAX register
when the string is printed and Reg2 will be in the EBX register. You can use
the special "#" and "?" characters in the string, identifying EAX (or a
subset of EAX) and EBX (or a subset) to insert the values of reg1 and reg2
in the string. By default, reg1=EAX and reg2=EBX. The default is used if you
do not specify the parameters to the macro.


17.6.3  Querying the VxD State

As described previously, one of the messages sent to the VxD_Control routine
is Debug_Query. This message is passed when you type ". name" to WDEB386.
See the sample sources for examples on how to use trace-out and the
In_Debug_Chr service to generate useful debugging information.

The .VMM command will give you a menu of useful information on the state of
the system as well as a way to toggle procedure logging.






Chapter 18  The VDD and Grabber DLL
────────────────────────────────────────────────────────────────────────────

This chapter describes the Virtual Display Device (VDD) and the Grabber DLL,
a Microsoft Windows dynamic-link library. Software writers should be
familiar with the terms and concepts covered in Chapter 16, "Overview of
Windows in 386 Enhanced Mode," and Chapter 17, "Virtual Device Programming
Topics," before continuing with this chapter.

The topics in this chapter are presented in the following order:


  ■   Introduction to VDDs and the Grabber DLL

  ■   VDD programming and the Grabber API

  ■   Grabber DLL interfaces




18.1  Introduction to VDDs and the Grabber DLL

The VDD and Grabber DLL are used by Windows when running in enhanced mode to
support a video adapter. The VDD is the virtual device that emulates display
hardware for applications running in virtual machines. The Grabber, like all
dynamic-link libraries, provides a library of routines available to
application programs. In this case, the application is the Windows WINOLDAP
program, and the routines are used primarily for rendering the video display
of a non-Windows application into a format that Windows can use.

The Windows 3.0 DDK provides source code for a variety of VDDs and Grabbers.
These should prove valuable as stubs and examples.


18.1.1  VDD Interaction with WINOLDAP

When Windows detects the user starting a non-Windows application, it runs
the Windows program called WINOLDAP and passes the name of the application
to it. WINOLDAP searches for a .PIF file and, then, makes a call to the
Shell device with all the parameters necessary to execute the application in
a new VM. The Shell makes a series of system control calls with the
appropriate messages to create and start the VM. It also calls the
VDD_PIF_State and VDD_Set_VM_Type services to let the VDD know the user
preferences for this VM.

WINOLDAP also loads the Grabber DLL, which it uses for rendering the VM's
display into the Windows display driver's bitmap format. The rendering is
done both for a full screen grab into the Clipboard (the VDD's grab routine
is called when this is done), and for displaying the VM in a window. This
rendering occurs in response to changes in video state caused by either the
VM's application or the user.

╓┌──────────────────────────┌──────────────────────────┌─────────────────────►
Cause of video state
change                     Example                    Effect
────────────────────────────────────────────────────────────────────────────
Application Program        Updating displayed         VDD sends WINOLDAP a
                           information                screen update message.

                                                      WINOLDAP calls the
                                                      Grabber to update the
                                                      window.

                                                      Grabber calls the VDD t
                                                      render the video state
                                                      into the application's
                                                      window.

User Action                Resizing a window          Windows sends WINOLDAP
                                                      paint message.

                                                      WINOLDAP calls the
                                                      Grabber to paint the
                                                      window.
Cause of video state
change                     Example                    Effect
                                                      window.

                                                      Grabber calls the VDD t
                                                      render the video state
                                                      into the application's
                                                      window.

────────────────────────────────────────────────────────────────────────────



Notice that the Grabber can be reentered. WINOLDAP may be in the process of
handling a paint message when it receives an update message. Care must be
exercised to ensure the correct handling of all the possible cases.


18.1.2  VDD User Messages

When the VDD encounters a situation that requires a user's choice or
interaction, it uses the Shell message services to print messages. For
example, when there is not enough memory to save and restore a VM's video
state, the user is informed of the problem and that a portion of the display
may be lost.

Messages are sent while in message mode, which is instigated with the
Begin_Message_Mode control call. Message mode enables the Shell device to
use the VDD message services to output text to the screen without changing
the VM's video state. When the message is complete, an End_Message_Mode
control call is made that restores the focus VM to the hardware. See Section
18.2.3, "Environment State Change Requirements for a VDD," for a description
of the message mode services.


18.1.3  VDD I/O Trapping and Hooked Pages

When an application is running in the background, the VDD traps all the
video I/O, saving the output port values and emulating the input port
values. In some cases, the detection of a mode change can result. In this
case, the memory should be disabled and hooked to enable the page fault
service to remap the memory.

A VDD should detect mode changes and illegal memory accesses. This is done
by disabling and hooking page faults that occur when the video memory is
accessed by the VM. The page fault service determines how to map the
accessed memory by both determining whether or not the VM has the display
focus and by examining the VM's virtual controller state. The page fault
service can also be used to demand page the video memory. It restores and
maps the video pages needed to create the physical display and to satisfy
the application's video memory accesses.


18.1.4  Grabber Naming Conventions

The names of the various supplied grabbers indicate in which mode they are
designed to run. For real and standard mode Windows, grabbers are named
FOO.GR2. For 386 enhanced mode Windows, they are named FOO.GR3.


18.2  VDD Programming

In addition to Chapter 17, "Virtual Device Programming Topics," which
discusses the general requirements for writing a VxD, valuable information
for the VDD author is located in Chapter 35, "Virtual Display Device (VDD)
Services."

The following are the recommended steps for developing a VDD:


  1.  Build a skeleton. Using the supplied sources as a guide, build a
      skeleton of the VDD with all the services and API procedures defined
      but not functional.

  2.  Add the initialization functionality, including the control block
      allocation, global memory needed, physical page hooking, I/O hooking,
      and interrupt hooking.

  3.  Fill out the services that handle the various hooks.

  4.  Test it while running Windows and other VMs, full screen.

  5.  Implement the Grabber API, including the procedures that report
      controller state, return video memory structures, and report video
      state modifications.

  6.  Test it while running VMs in a window. Do a thorough test, running
      many different applications in all the different states (i.e.,
      exclusive, background, and windowed), while using ALT+TAB and directed
      hot keys to switch VMs. Be sure to test various error conditions such
      as being out of memory.



18.2.1  VDD Efficiency

To maximize the efficiency of Windows, a VDD is, in many cases, tightly
coupled with the Windows 3.0 display driver. For instance, the EGA display
would normally have to be trapped at all times to maintain the controller
state properly. Instead, an API has been defined for communications between
the Windows display driver and the VDD. Additionally, the EGA Windows
display driver uses a special portion of video memory and a special
algorithm that allows for a subset of the video controller state to be saved
and restored without explicitly saving away the current register values.
When adapting a VDD to new displays, it is a good idea to look at
alternatives to trapping all the display adapter access to maintain the
video state. Notice also that the Grabber is usually tightly coupled to the
Windows display driver, specifically to the display-dependent bitmap format.


There are three PIF bits that the user can specify to disable trapping in
VMs where the applications running in the VMs only modify registers that can
be read. The VDD designer should use these PIF bits, if possible. There are
additional PIF bits by which the user can specify the amount of video memory
a VM will need. See the Microsoft Windows User's Guide for more information
on PIF files.

Another good area in which to consider optimizing is the API emulation,
especially the INT 10H Write TTY function. The user can specify this
emulation with a PIF bit.


18.2.2  Converting Your 2.x VDD

Even though the general structure of Windows 3.0 VxDs is significantly
different from 2.x versions, the low-level VDD routines of previous
versions, such as the saving, restoring, and trapping routines, should work
with little modification. However, significant changes have occured in how
VxDs interact with the operating environment. For example, the VDD is now a
separate .386 file linked dynamically to 386 enhanced mode Windows.
Therefore, a recommended development strategy is to insert the 2.x routines
into one of the supplied 3.0 VDD sources.

The 3.0 Grabber's interface with WINOLDAP and the VDD is also different. The
most significant change is that in version 2.x the Grabber was not a DLL.
See Section 18.3, "Grabber DLL Interfaces," for more detailed information.

The following topics describe areas of a 2.x VDD that will require
modification.


INCLUDE Files

Most of the modules only need VMM.INC, VDD.INC, DEBUG.INC, and a device
specific INCLUDE file (e.g., EGA.INC). Some modules also require a file
describing the interface between them and some external user of their
functions (e.g., VMDAEGA.INC for the Grabber). By changing over to the new
INCLUDE files, you will generate several undefined references. Modifying the
references to use the equivalent Windows 3.0 functionality is a first step
in creating your Windows 3.0 VDD.


System Interface

Examine the parts of the supplied Windows 3.0 VDD to understand the new
system interface. You will need the VDD_Init, VDD_New, VDD_Exit,
VDD_Destroy, VDD_SetType, and VDD_SetFocus services to use the new device
control interface. The functionality of VDD_Install should be handled by
scheduling VM events. The VDD_Mem_Check service is replaced by the VDD
specifically calling the Shell to give the user a message. VDD_CHK_Device is
also replaced by sending WINOLDAP a message when the display needs to be
updated and by scheduling time outs to do the detection. The register values
and what you can and cannot do in an I/O trap, page fault, and interrupt
trap are also changed. Mostly, there is much more flexibility allowed, and
there are changes in register save/restore and parameter passing
conventions.

Previously, VDD provided a single VDD_Control with various subfunctions.
Most of the VDD_Control calls are replaced by the device API mechanism.

Notice that the definition of exclusive is different for Windows 3.0 and
that the SetFocus service takes into account whether or not the VM is
running in a window (i.e., VDD will get a SetFocus call for the System VM on
a VM that is running in a window, instead of a SetFocus call to the VM
itself).


Shell

The Shell device requires a number of new functions that are implemented as
device services. Additionally, the old ID call is device service 0.


Grabber

Notice that the way that the services retrieve the Grabber DLL's registers
is different (i.e., by using EBP and the Client_Reg definitions). Also
notice the increased number of functions and other changes in the
functionality of the Grabber DLL interface.


Memory Access

Use the _MapPhysToLinear function rather than adding PhysToLinr to the
physical addresses. You should also add the control block value
CB_High_Linear to the BIOS memory address for accessing those memory
locations.

Since memory in the control block is allocated dynamically, the address of
your portion of the control block must be formed at run time, not compile
time.


18.2.3  Environment State Change Requirements for a VDD

During Windows initialization, VM creation and termination, and other status
changes of the Windows environment, there are certain programming
requirements for display devices. The following includes descriptions of
typical requirements for various states. Use the EGA/VGA and CGA sources as
examples. Specific interfaces to the described routines are included in
Section 17.1, "Writing VxDs."


Real Mode Initialization

Most video adapters will not need any real-mode code to be functional.
However, a few developers will want to add some real-mode code to query
device state or to reserve portions of memory that may not be touched during
the initialization of other devices or used for general system purposes. For
example, you may have a memory-mapped interface that will be harmed by other
code reading and writing at those addresses (Windows in 386 enhanced mode
searches the area between C0000H and EFFFFH for the existence of RAM or
ROM). If the VDD supports multiple display adapters, display-adapter
detection can be done here by passing the result of the detection in the EDX
register to the protected-mode initialization.


Sys_Critical_Init

During Sys_Critical_Init, a VDD should allocate its control block data area,
allocate V86 address space, allocate memory needed globally, and initialize
any pointers or other data that are required for the VDD functionality.
Remember that interrupts are disabled during this call, so keep it as short
as possible.


Device_Init

During Device_Init, a VDD should finish initializing its global state, set
up the I/O and interrupt trapping needed, and specify instance data. As
noted in Chapter 16, "Overview of Windows in 386 Enhanced Mode," this
initialization call is equivalent to VMCreate for the System VM, along with
the global device initialization. Finally, the VDD should set the display
focus to the system VM internally.


Init_Complete

During Init_Complete, a VDD should do any consistency checks that have to be
done after all the other devices have completed their initialization.
Normally, a VDD will not need to do anything with this control call.


Sys_VM_Init

During Sys_VM_Init, a VDD should initialize the rest of the control block
data for the system VM, and set the display focus to the system VM.


VM_Create

During creation, initialize the control block and allocate any VM specific
memory. If the allocation fails, return the Carry flag set to abort the VM
creation.


VM_Init

During initialization, set the video state of the VM, typically by making
calls to the Video BIOS and trapping the I/O to set up the video state
structure.


Destroy_VM

During destruction, deallocate any memory allocated for the VM and make sure
that there are no pointers left that refer to the destroyed VM.


Set_Device_Focus

The SetFocus service is responsible for giving the specified VM the physical
display. Notice that there is display SetFocus and critical SetFocus. Both
should give the physical display to the indicated VM. Also notice that the
actual restoring of the physical display should occur by executing the
VDD_Restore service as an event.


VM_Suspend

During VM_Suspend, a VDD should unlock any memory associated with the VM,
such as the memory used to save and restore the video state.


VM_Resume

During VM_Resume, a VDD should lock the memory unlocked during suspend.


System_Exit

During System_Exit, a VDD should return the display to the state it should
be in when Windows returns to MS-DOS.


Sys_Critical_Exit

During Sys_Critical_Exit, a VDD should restore any hooks remaining in the
V86 memory.


Begin_Message_Mode

After saving the current VM's video state, the VDD should put the display
adapter into a known text mode. If this is called prior to the point in
initialization (System VM Initialization) at which a known text mode has
been saved, call the video BIOS to set up the correct mode.

The message mode services, VDD_Msg_ClrScrn, VDD_Msg_ForColor,
VDD_Msg_BakColor, VDD_Msg_TextOut, and VDD_Msg_SetCursPos are only used
after the Begin_Message_Mode message has been received. See Chapter 35,
"Virtual Display Device (VDD) Services," for details.


End_Message_Mode

During End_Message_Mode, a VDD should restore the focus VM's video state.


Debug_Query

In a debug version of your VDD, use the Trace_Out macro to list the current
display owner, other potentially interesting global data, and the video
state for each VM.


18.2.4  Grabber API

The Grabber uses the VDD's Get_Version service to verify that it is matched
with the correct VDD. Whenever the Grabber needs access to the video memory
or the video controller state, it queries the VDD. The VDD returns a data
structure describing the requested memory or controller state.

Get_Mem is used to get the current contents of the video memory while
updating the windowed display. Get_GrbMem is used to get a snapshot of the
entire screen in response to an ALT + PRTSCN command from the user in a full
screen VM. Free_Mem and Free_Grab are used to tell the VDD that the grabber
is no longer using this memory. Get_State and Get_GrbState return the
current and grabbed controller states, respectively.

Get_Mod is used to update the windowed display incrementally. Get_Mod
returns a data structure that indicates modifications to the current
display. The Grabber DLL modifies only those parts of the window that have
changed and, then, issues a Clear_Mod call to inform the VDD that the
modifications have been carried out.

To make sure that the video memory or state will not change when the Grabber
is accessing the memory, the VM should not be running after a Get_Mem or
Get_Mod call. The VM can continue to run only after a Free_Mem call or an
explicit Unlock_App call from the Grabber.


18.3  Grabber DLL Interfaces

The Grabber is a dynamic-link library (DLL) primarily responsible for
representing the VM's display state to the Windows display driver. It is the
library of functions used by WINOLDAP, the Windows program responsible for
creating, destroying, and changing the state of VMs. WINOLDAP makes private
calls to the Shell device, which in turn calls the necessary VMM services.
Therefore, it is WINOLDAP, using the Grabber (and through it the Windows
display driver), that is actually responsible for windowing the display
state of a VM.

Each of the Grabber functions is a cProc and has to be exported. The
function code can be shared by several instances of WINOLDAP, and therefore,
the placement of VM-specific data must be deliberate. The Grabber DLL
functions provide support for the following:


  ■   Screen grabbing

  ■   Marking and selecting

  ■   Painting non-Windows applications in a window

  ■   Doing other miscellaneous functions


The Grabber generates data in the following situations:


  ■   When an Extended Paint structure (EXTPAINTSTRUC) is passed from
      WINOLDAP

  ■   When a procedure requires local data. (Local data is maintained on the
      stack.)



18.3.1  On-Screen Selection Functions

The user can make on-screen selections with either the keyboard or mouse, or
through a hot key. The keyboard or mouse are used only while in a window; a
hot key (ALT+PRTSCRN) is used while in full-screen or windowed mode.

The functions that handle on-screen selections are as follows:


  ■   BeginSelection

  ■   EndSelection

  ■   KeySelection

  ■   AdjustInitEndPt

  ■   MakeSelctRect


To perform a selection by using the keyboard, the user performs the
following steps:


  1.  Choose the Mark command.

  2.  Move the cursor to the start point of the selection.

  3.  Sweep through a selection using SHIFT + DIRECTION keys.

  4.  Press ENTER to end a selection and copy it to the Clipboard (or press
      ESC to end the selection without a copy).


On choosing Mark from the menu, BeginSelection gets called with argument
<0,0>.

During the first phase, the cursor is moved to the actual start point.
KeySelection handles the cursor movement. It returns the new start point
every time a DIRECTION key is pressed. Notice that the selection could
potentially begin at each cursor position. Therefore, every time the start
point is changed, EndSelection is called to cancel the previous selection,
and BeginSelection is called with the new start point.

Once the cursor is positioned at the actual start point, the user sweeps
through a selection area using SHIFT+DIRECTION keys. KeySelection handles
the cursor movement. It returns the new end point of the selection. Now each
call to KeySelection is followed by a call to MakeSelctRect to record the
current selection rectangle. On pressing ENTER, the actual end point and the
final selection rectangle are established.

Therefore, the last call to BeginSelection establishes the actual start
point, the last call to KeySelection returns the actual end point, and the
final call to MakeSelctRect records the actual selection rectangle. If only
DIRECTION keys are pressed, the user is shifting the start point. If
SHIFT+DIRECTION keys are pressed, the user is changing the active end point.


────────────────────────────────────────────────────────────────────────────
NOTE

The start point and the end point of a selection have to be aligned on
character boundaries in text mode. In graphics mode, the Grabber chooses
some granularity for cursor movement (e.g., DWORD of pixels).
────────────────────────────────────────────────────────────────────────────

The coordinates of the start point and the end point are given in screen
coordinates ─ a window client area position corrected by the scroll bar
position. Client area coordinate = <0,0> corresponds to the screen
coordinate <ColOrg,RowOrg>. (ColOrg and RowOrg are available in the extended
paint structure.)


18.3.2  Selection Functions

This section presents descriptions of the selection functions in
alphabetical order.


AdjustInitEndPt
────────────────────────────────────────────────────────────────────────────


Description

This function adjusts the initial selection end point. To start off, the
start point and the end point are the same. (This is how BeginSelection
records them). On the first SHIFT + DIRECTION key call to KeySelection,
notice that KeySelection returns the wrong end point. This function returns
the correct end point. It returns (X+DELTAx, Y+DELTAy) where <X,Y > is the
given end point. DELTAx and DELTAy are as defined in KeySelection.


Entry

lpPntStruct = EXTPAINTSTRUC

YCoOrd,XCoOrd = (Y,X) point to be adjusted


Exit

DX,AX = (Y,X) end point adjust down and to right for initial selection.


BeginSelection
────────────────────────────────────────────────────────────────────────────


Description

This function starts the selection at the indicated point.


Entry

lpPntStruc = EXTPAINTSTRUC

YCoOrd,XCoOrd = (Y,X) screen coord of start pt


Exit

[lpPntStruc.SelStruc.SelctSRect] Display rectangle in the EXTPAINTSTRUC
selection structure set


ConsSelecRec
────────────────────────────────────────────────────────────────────────────


Description

This function makes the display rectangle consistent with the selection.


Entry

lpPntStruc = EXTPAINTSTRUC


Exit

[lpPntStruc.SelStruc.SelctSRect] Display rectangle in the EXTPAINTSTRUC
selection structure set


EndSelection
────────────────────────────────────────────────────────────────────────────


Description

This function stops the selection.


Entry

lpPntStruc = EXTPAINTSTRUC


Exit

None


InvertSelection
────────────────────────────────────────────────────────────────────────────


Description

This function inverts the selection.


Entry

lpPntStruc = EXTPAINTSTRUC


Exit

DX,AX = (Y,X) screen CoOrd of "active" selection endpoint


KeySelection
────────────────────────────────────────────────────────────────────────────


Description

This function is for keyboard selection.


Entry

lpPntStruc = EXTPAINTSTRUC

StartType = 0 if SHIFT key UP  != 0 if SHIFT key DOWN

╓┌─────────────────────────────────┌─────────────────────────────────────────╖
────────────────────────────────────────────────────────────────────────────
MFunc                             = 0 To Right
                                  = 1 To Left
                                  = 2 Down
                                  = 3 Up

MFunc                             = 0 To Right
                                  = 1 To Left
                                  = 2 Down
────────────────────────────────────────────────────────────────────────────
                                  = 2 Down
                                  = 3 Up




Exit

DX,AY = (Y,X) screen CoOrd of new select end pt

KeySelection responds to DIRECTION keys and SHIFT+DIRECTION keys.

DIRECTION key response: (SHIFT key UP)

if (LEFT Key)  return (X-DELTAx, Y) else if (RIGHT Key)  return (X+DELTAx,
Y) else if (DOWN Key)  return (X, Y+DELTAy) else if (UP Key)  return (X,
Y-DELTAy);

where <X,Y> is current end point.

DELTAx DELTAy are the font width and height in text mode and some
appropriate value in graphics mode.

SHIFT+DIRECTION key response:

Similar to above except <X,Y> is current end point.


MakeSelctRect
────────────────────────────────────────────────────────────────────────────


Description

This function sets a new selection. It is called after every call to
KeySelection in response to SHIFT+DIRECTION key. Given a new end point, it
adjusts the new end point to be character-aligned in text mode (and on a
convenient boundary in the video memory in graphics mode). It also adjusts
for screen maxima. It sets the Selection rectangle based on the current
start point and end point.


Entry

lpPntStruc = EXTPAINTSTRUC

YCoOrd,XCoOrd = (Y,X) screen CoOrd of new end point


Exit

[lpPntStruc.SelStruc.SelctSRect], Display rect in extended paint selection
structure set

AX == 0, if no change was made to selection parameters

────────────────────────────────────────────────────────────────────────────
NOTE

[lpPntStruc.SelStruc.SelctSRect] must still be set in this case.
────────────────────────────────────────────────────────────────────────────


RenderSelection
────────────────────────────────────────────────────────────────────────────


Description

This function renders the selection into the Clipboard format.


Entry

lpPntStruc = EXTPAINTSTRUC

╓┌─────────┌─────────────────────────────────────────────────────────────────╖
────────────────────────────────────────────────────────────────────────────
wParam    Parameter from VDD message (= -1 if VMDOSAPP origin
lParam    Parameter from VDD message (=0 if VMDOSAPP origin) Event ID



Exit

if (DX < 0)  Error else if (DX = 0)  No Selection else if (DX > 0)  DX =
format, (CF_OEMTEXT or CF_BITMAP)  AX = Handle, (Memory Handle or Bitmap
Handle)


18.4.1  Application Painting Function

This section presents a description of the non-Windows application painting
function.


GetDisplayUpd
────────────────────────────────────────────────────────────────────────────


Description

This function calls the VDD to get a display update (if any) and stores it
in the PAINT structure.

It prevents any further changes from occurring in the application. The
application restarts after a call to one of the following; UpdateScreen,
PaintScreen, or GrbUnLockApp.


Entry

lpPntStruc = EXTPAINTSTRUC

╓┌─────────┌─────────────────────────────────────────────────────────────────╖
────────────────────────────────────────────────────────────────────────────
wParam    Parameter from VDD message (= -1 if VMDOSAPP origin)
lParam    Parameter from VDD message (=0 if VMDOSAPP origin) Event ID



Exit

AX = Display update flags (see grabpnt.inc for fDisp_ flags)


Comments

This call "locks" the application (prevents further changes from occuring).
The application can be started again by a call to one of the following:


  ■   UpdateScreen

  ■   PaintScreen

  ■   GrgUnLockApp



18.4.1  Miscellaneous Functions

This section presents descriptions of miscellaneous functions in
alphabetical order.


CheckGRBVersion
────────────────────────────────────────────────────────────────────────────


Description

This function checks out the VDD version.


Entry

lpPntStruc = EXTPAINTSTRUC


Exit

If (AX == 0)  OK AX == 1  Version # error AX == 2  Display type mismatch
(VDD and Grabber are not compatible)  DX = Grabber Version number


CursorOff
────────────────────────────────────────────────────────────────────────────


Description

This function destroys the cursor for an application.


Entry

lpPntStruc = EXTPAINTSTRUC


Exit

Caret destroyed


CursorOn
────────────────────────────────────────────────────────────────────────────


Description

This function creates the cursor for an application if it has one.


Entry

lpPntStruc = EXTPAINTSTRUC


Exit

Caret created


CursorPosit
────────────────────────────────────────────────────────────────────────────


Description

This function returns the position of the cursor on the display.


Entry

lpPntStruc = EXTPAINTSTRUC


Exit

DX,AX = (Y,X) screen CoOrd of upper left of cursor

 = (-1,-1) if no cursor


GetFontList
────────────────────────────────────────────────────────────────────────────


Description

This function returns a pointer to the list of extra fonts you want loaded.



Entry

lpFontBuf -> Buffer for font info


Exit

Font Buffer filled in


GrabComplete
────────────────────────────────────────────────────────────────────────────


Description

This function signals that you are finished with the grab. This is called
after the grab is complete. It is time to call the VDD and have it free the
grab memory.


Entry

lpPntStruc = EXTPAINTSTRUC

╓┌───────────┌───────────────────────────────────────────────────────────────╖
────────────────────────────────────────────────────────────────────────────
wParam      Parameter from VDD message (= -1 if VMDOSAPP origin)
lParam      Parameter from VDD message EVENT ID



Exit

None


GrabEvent
────────────────────────────────────────────────────────────────────────────


Description

This function provides a private channel of event communication between the
VDD and the Grabber to perform a hot key screen grab.


Entry

lpPntStruc Extended paint structure

╓┌─────────────────┌─────────────────────────────────────────────────────────╖
────────────────────────────────────────────────────────────────────────────
wParam            Parameter from VDD message
lParam            Parameter from VDD message EVENT ID



Exit

None


GrbUnLockApp
────────────────────────────────────────────────────────────────────────────


Descripton

This function undoes the implied application lock of GetDisplayUpd.


Entry

lpPntStruc = EXTPAINTSTRUC


Exit

None


InitGrabber
────────────────────────────────────────────────────────────────────────────


Description

This is the library initialization function.


Entry

DI = Module handle of the library

CX = Size of local heap (should be 0)

DS = Seg addr of library data segment (isn't one)


Exit

AX == 0  Init Error AX != 0  OK


PaintScreen
────────────────────────────────────────────────────────────────────────────


Description

This function paints the indicated region of the screen.

This procedure paints the non-Windows application screen into a window. The
origin of this is a Windows paint as opposed to a display update, which is
handled at UpdateScreen. When a non-Windows application receives a Windows
paint message, Paint Screen gets called.


Entry

lpPntStruc = EXTPAINTSTRUC


Exit

AX != 0 Screen Painted

AX == 0 Screen not painted, probably insufficient Windows memory problem


ScreenFree
────────────────────────────────────────────────────────────────────────────


Description

This function frees anything associated with this application.


Entry

lpPntStruc = EXTPAINTSTRUC


Exit

Any allocated stuff associated with the application is freed.


SetPaintFnt
────────────────────────────────────────────────────────────────────────────


Description

This function sets the font for painting in the extended paint structure so
that WINOLDAP can compute the paint rectangle for use on PaintScreen calls.
This is called right before a call to PaintScreen. It is also called right
before a call to UpdateScreen.


Entry

lpPntStruc = EXTPAINTSTRUC

╓┌────────────────────────┌──────────────────────────────────────────────────╖
────────────────────────────────────────────────────────────────────────────
lpWidFullScr             Word pointer for width return
lpHeightFullScr          Word pointer for height return



Exit

FntHgt and FntWid values in EXTPAINTSTRUC set

────────────────────────────────────────────────────────────────────────────
NOTE

Values are set to 0 if it is a graphics screen.
────────────────────────────────────────────────────────────────────────────

[lpWidFullScr] = Width of full screen in pix (Text or Graphics)

[lpHeightFullScr] = Height of full screen in pix (Text or Graphics)

DX is height of full screen in scan lines if Graphics, in text lines if
Text.

AX is width of full screen in pix if Graphics, in chars if Text.


UpdateScreen
────────────────────────────────────────────────────────────────────────────


Description

This function updates changed portions of the screen. When a non-Windows
application modifies the display on its own, UpdateScreen is called.


Entry

lpPntStruc = EXTPAINTSTRUC


Exit

AX == 1 if Screen Paint, unless fGrbProb bit set in EPStatusFlags

AX == 0 if Screen not painted, probably low Windows memory problem






PART IV  Virtual Device Services
────────────────────────────────────────────────────────────────────────────

This part documents all the enhanced Windows virtual machine environment
services. They are grouped by service type and presented in the order shown
on the following page.

See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.






Chapter 19  Memory Management Services
────────────────────────────────────────────────────────────────────────────

Enhanced Windows supplies a rich set of memory management services. Since
many of the services are unnecessary for most VxD development, only a
commonly used subset is listed in this introduction. However, all the memory
management services are documented in either this chapter or in Chapter 40,
"V86 Mode Memory Manager Device Services."

See also Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter
17, "Virtual Device Programming Topics," for general environment
discussions. Memory management is also discussed in the Microsoft Windows
Software Development Kit, Programming Tools and in Chapter 6, "Network
Support," in the Microsoft Windows Device Driver Adaptation Guide.

The enhanced Windows environment uses a virtual memory scheme capable of
overcoming the limits of actual physical memory. Though it may not be
physically present, a virtual memory of 4 gigabytes is theoretically
addressable. This is done by swapping (paging) code and data to and from RAM
and a secondary storage device. Since VxDs reside within the 32-bit
protected-mode portion of the environment, they can directly access all of
the memory, but should only access memory whose address was obtained through
the memory management services.

Windows determines the amount of virtual memory actually available based on
the total amount of physical memory on the system and the amount of disk
space available. This can be changed (downward) by modifying the swap file
size specified in the SYSTEM.INI file, or by running the SWAPFILE program
(documented in the Microsoft Windows User Guide).

The memory manager will continue to allocate physical memory until it has
been used up. Then, it will begin moving 4-kilobyte pages of code and data
from physical memory to disk to make additional physical memory available.
Windows pages in 4-kilobyte blocks, rather than unequal-sized code and data
segments. The swapped 4-kilobyte block may be only part of a given code or
data segment, or it may cross over two or more code or data segments.

This memory paging is transparent to a program. If an attempt is made to
access a code or data segment of which some part has been paged out to disk,
the 80386 issues a page fault interrupt. The memory manager then swaps other
pages out of memory and restores the pages that the program needs.

The Windows memory management services are presented in the following
categories. The services listed comprise the commonly used subset.


  ■   System Data Object Management

      Allocate_Device_CB_Area

  ■   Device V86 Page Management

      Assign_Device_V86_Pages

  ■   GDT/LDT Management

  ■   System Heap Allocator

      HeapAllocate

      HeapFree

  ■   System Page Allocator

      CopyPageTable

      MapIntoV86

      ModifyPageBits

      PageAllocate

      PageFree

      PageLock

      PageUnlock

      PageGetAllocInfo

      PhysIntoV86

  ■   Looking at Physical Device Memory in Protected Mode

      MapPhysToLinear

  ■   Data Access Services

      GetFirstV86Page

  ■   Special Services for Protected Mode APIs

  ■   Instance Data Management

  ■   Looking at V86 Address Space




19.1  System Data Object Management

These services provide support for allocating special system areas. The two
areas managed are the Control Block and the Global V86 Addressable Area.

────────────────────────────────────────────────────────────────────────────
NOTE

All of these calls use the USE32 C calling convention. The true name of the
procedure has an underscore in front (i.e., Allocate_Device_CB_Area is
actually _Allocate_Device_CB_Area), and the arguments are pushed right to
left (unlike the PL/M calling convention used by Windows, which is left to
right). The return value(s) is returned in C standard EDX:EAX. It is the
responsibility of the callerto clear the arguments off the stack. Registers
EAX, ECX, and EDX are changed by calls. Registers DS, ES,
FS, GS, EBP, EDI, ESI, and EBX are preserved.

────────────────────────────────────────────────────────────────────────────

Allocate_Device_CB_Area
────────────────────────────────────────────────────────────────────────────

  unsigned Allocate_Device_CB_Area(nBytes,flags)
  unsigned nBytes;
  unsigned flags;

This call is used to allocate a region of the Control Block data structure
to a particular device. Devices typically want some data that is "per VM".
For example, a device which is virtualizing a particular set of I/O ports
for the VM needs a place to store each VMs "instance" of the I/O port state.
This is done by allocating a region of the VM Control Block large enough to
hold a device specific data structure which contains the state. For example,
if the device specific data structure looks like this:

  FooDeviceCB Struc
   FooDevReg1  db ? ; Dev I/O register 1
   FooDevReg2  db ? ; Dev I/O register 2
   FooDevReg3  db ? ; Dev I/O register 3
   FooDevReg4  db ? ; Dev I/O register 4
   FooDevState  dd ? ; State flags for device
  FooDeviceCB Ends

Space in the VM Control Block would be allocated like this:

  VxD_DATA_SEG
  FooDevCBOffset dd ?
  VxD_DATA_ENDS
  VxD_ICODE_SEG
  ;
  ; Allocate the Control Block space. This is in Foo's INIT routine
  ;
   VMMCall _Allocate_Device_CB_Area,<<SIZE FooDeviceCB>,0>
    or eax,eax
    jz short No_CB_Space_Error ; Probably FATAL error
    mov [FooDevCBOffset],eax

  VxD_ICODE_ENDS

  VxD_CODE_SEG
  ;
  ; In VxD procedures the Control Block pointer is passed
  ;   in EBX the control block may be pointed to like this.
  ;
   mov edx,ebx
    add edx,[FooDevCBOffset]
    mov al,[edx.FooDevReg1]
    ...

  VxD_CODE_ENDS

The nBytes parameter specifies the number of bytes of space to be allocated.
There are currently no bits defined in the flags, this parameter must be set
to 0.


Return Value

Returns nonzero Control Block Offset of the block allocated if successful,
returns zero if the space could not be allocated (This is probably a fatal
error, it is up to the caller to decide what is to be done in this case).


Comments

Control block Offsets returned from this call will be DWORD aligned. The
nBytes parameter does not have to be a multiple of 4, but if it isn't, it
will currently be rounded up to a multiple of 4. This may change in a later
releases, so do no depending one rounding.

The above code sample is not the only way to do things. There are many other
ways the control block offset value can be used to access your devices
specific region of the control block.

────────────────────────────────────────────────────────────────────────────
NOTE

This routine itself is in the init segment of enhanced Windows. It can
therefore only be called during system initialization. Trying to call it
after system initialization and the system INIT segment space has been
reclaimed will result in a fatal page fault.
────────────────────────────────────────────────────────────────────────────

When Control Block regions are allocated they are initialized with value 0
in all bytes. When new VMs are created, all bytes of the Control Block are
set to 0.


Allocate_Global_V86_Data_Area
────────────────────────────────────────────────────────────────────────────

  unsigned Allocate_Global_V86_Data_Area(nBytes,flags)
  unsigned nBytes;
  unsigned flags;

This call is used to allocate a region of the Global V86 Addressable Area to
a particular device. This area is used for device specific objects which
must also be addressable by the Virtual mode code running in the Virtual
Machine.

An example is a Virtual mode software interrupt which is trapped by the
device and causes the return of a Virtual mode pointer to some data
associated with the device. The data must be in the VM's V86 address space
since a Virtual mode pointer to it is returned. In this case there is no
reason for the interrupt hook code to also be in the Global V86 Addressable
Area, that can all be in the protected mode device.

The nBytes parameter specifies the number of bytes of space to be allocated.
Current flags bits:

  GVDAWordAlign EQU 00000000000000000000000000000001B GVDADWordAlign EQU
00000000000000000000000000000010B GVDAParaAlign EQU
00000000000000000000000000000100B GVDAPageAlign EQU
00000000000000000000000000001000B GVDAInstance EQU
00000000000000000000000100000000B GVDAZeroInit EQU
00000000000000000000001000000000B GVDAReclaim EQU
00000000000000000000010000000000B

All unused bits must be zero. GVDAxxxxAlign bits specify the indicated
alignment (WORD, DWORD, PARAGRAPH, PAGE) for the start of the block. If none
are set, BYTE alignment is assumed. GVDAInstance, if set, indicates that the
block is an item of VM instance data for which each different VM has its own
private values. If GVDAInstance is clear, the block is global data and all
VMs share the same value setting. GVDAZeroInit, if set, indicates that the
block is to initialized with value 0 in all bytes of the block. If
GVDAZeroInit is clear, the block will have random values in it.

GVDAReclaim is only valid if GVDAPageAlign is set. IF GVDAReclaim is set,
then the physical pages of the region should be "reclaimed" by the MMGR
(memory manager) and placed on the free list, and the NUL page should be
mapped in the region.


Return Value

Returns nonzero linear address of the block allocated if successful, returns
zero if the space could not be allocated. This is probably a fatal error; it
is up to the caller to decide what is to be done in this case.


Comments

The Flag bit equates are defined by including VMM.INC. The equates should be
used.

For blocks allocated with GVDAInstance set, the AddInstanceItem call is made
by this routine for you.

Note the interaction with Allocate_Temp_V86_Data_Area.

Specifying multiple GVDAxxxxAlign bits will result in random behavior. At
most ONE of these bits must be set.

The returned linear address is a ring 0 linear address. It is up to the
caller to convert this into a Virtual mode SEG:OFFSET form if that is
needed.

The linear addresses returned by this call will be

Generally only data needs to be placed in these blocks, but code can be
placed if desired.

────────────────────────────────────────────────────────────────────────────
WARNING

You must be very careful if allocating two blocks, one for code which is not
 instanced, and one for data which is Instanced because you cannot assume
that the two blocks will be within 64K of each other and thus addressable
with the same segment register in virtual-80 mode.
────────────────────────────────────────────────────────────────────────────

If the VxD desires the values of Instance fields allocated with this call to
have a set initial value whenever a new VM is created, the field must be
initialized with the desired values immediately after making this call. The
contents of the instance blocks at the time VxD initialization is completed
is what each new VM is created with.

────────────────────────────────────────────────────────────────────────────
NOTE

This routine itself is in the init segment of enhanced Windows. It can
therefore only be called during system initialization. Trying to call it
after system initialization and the system INIT segment space has been
reclaimed will result in a fatal page fault.
────────────────────────────────────────────────────────────────────────────


Special notes for GVDAPageAlign

This type of allocation is intended to support Vxds which need a global page
aligned piece of V86 address space where they can MapIntoV86 data. The best
example of such a VxD is the PageSwap device.

The nBytes parameter should be a multiple of 4096 (page size).

Note that this page is global but that MapIntoV86, PhysIntoV86, and
LinMapIntoV86 are calls which are local to a specific VM. This means that a
VxD which wishes to globally change the mapping of this region must traverse
the VM list with Get_Next_VM_Handle and perform the map in each VM
individually.

────────────────────────────────────────────────────────────────────────────
WARNING

Do not issue any of the map calls on this region before SYS_VM_Init device
call time. Failure to follow this rule can cause the page type bits in the
page table to get set improperly.
────────────────────────────────────────────────────────────────────────────

VxDs using this should set the correct initial VM state in their Create_VM
device call code. The initial state of the region is actually a copy of the
current state of SYS_VM_Handle, but you should not rely on this. Set the
initial state you want explicitly by making a MapIntoV86, or PhysIntoV86
call.

The physical page(s) which are mapped into this region at the time you
allocate it are not pages that the MMGR worries about. It is up to the VxD
to put the physical pages to good use. The addresses of these physical
pages(s) is found by doing a CopyPageTable call on the SYS_VM_Handle and
looking at the physical address in the page table entries.

Do not assume that the physical addresses of these pages equals the linear
address returned. This will be true on most machines, but not on some. These
pages by using are mapped with PhysIntoV86.

If GVDAReclaim is set, then the physical pages that currently are mapped in
the region will be reclaimed by the MMGR and placed on the free list. The
NUL page will then be mapped in the region.

If GVDAReclaim is clear, the physical page(s) which are mapped into this
region at the time you allocate it are not pages that the MMGR worries
about. It is up to the VxD to use these physical pages for something useful.
Avoid wasting them. The addresses of these physical pages(s) is found by
doing a CopyPageTable call on the SYS_VM_Handle and looking at the physical
address in the page table entries.

It is invalid to assume that the physical addresses of these pages = the
linear address returned. This will be true on most machines, but on some it
will not. These pages are mapped using PhysIntoV86.

You will not be able to Assign_Device_V86_Pages the pages of this region.
They are already marked as globally owned because they are below
FirstV86Page.

You cannot set both GVDAReclaim and GVDAInstance. Attempting to do so will
result in an error.


Allocate_Temp_V86_Data_Area
────────────────────────────────────────────────────────────────────────────

  unsigned Allocate_Temp_V86_Data_Area(nBytes,flags)
  unsigned nBytes;
  unsigned flags;

This call is used to allocate a region of the Global V86 Addressable Area to
a particular device during system initialization.

The primary reason for allocating this area is to create a buffer into which
data associated with some Simulate_Int activity (like an INT 21H MS-DOS
system call) can be placed. The area allocated with this call only exists
for a short period of time during initialization. The nBytes parameter
specifies the number of bytes of space to be allocated. There are currently
no bits defined in the flags, this parameter must be set to 0.


Return Value

Returns nonzero linear address of the block allocated if successful, returns
zero if the space could not be allocated (insufficient memory, or temp area
already allocated).


Comments

There is only one Temp area, therefore only one allocation will be allowed
to be outstanding at a time. Attempts to allocate the Temp area when it is
already allocated will result in an error.

The Allocate_Global_V86_Data_Area call does not function while the Temp Area
is allocated. The Temp Area must be released with Free_Temp_V86_Data_Area
before the Allocate_Global_V86_Data_Area call can be made again.

Make sure you Free_Temp_V86_Data_Area the temp area as soon as possible.

The returned linear address is a ring 0 linear address. It is up to the
caller to convert this into a Virtual mode SEG:OFFSET form if that is
needed.

The linear address returned by this call will be

Since this area exists only temporarily, it doesn't make sense to instance
any of it.

The linear address returned from this call is paragraph aligned.

The contents of the block will always be zero initialized by this call.

────────────────────────────────────────────────────────────────────────────
NOTE

This routine itself is in the init segment of enhanced Windows. It can
therefore only be called during system initialization. Trying to call it
after system initialization and the system INIT segment space has been
reclaimed will result in a fatal page fault.
────────────────────────────────────────────────────────────────────────────


Free_Temp_V86_Data_Area
────────────────────────────────────────────────────────────────────────────

  unsigned Free_Temp_V86_Data_Area()

This call is used to free the Temp_V86_Data_Area allocated with
Allocate_Temp_V86_Data_Area.


Return Value

Returns nonzero if successful, returns zero if unsuccessful (Temp Area not
allocated).


Comments

The Allocate_Global_V86_Data_Area call does not function while the Temp Area
is allocated. The Temp Area must be released with Free_Temp_V86_Data_Area
before the Allocate_Global_V86_Data_Area call can be made again.

Once this call is issued, the Linear Address that was returned from
Allocate_Temp_V86_Data_Area can no longer be used for anything. The system
will probably crash if this is attempted.

────────────────────────────────────────────────────────────────────────────
NOTE

This routine itself is in the init segment of enhanced Windows. It can
therefore only be called during system initialization. Trying to call it
after system initialization and the system INIT segment space has been
reclaimed will result in a fatal page fault.
────────────────────────────────────────────────────────────────────────────


19.2  Device V86 Page Management
────────────────────────────────────────────────────────────────────────────

Certain types of VxDs may want to take control of certain regions of the
VM's virtual-86 address space for use by the VxD. The best examples of this
are as follows:


  ■   The display device (VDD), which wants to reserve those areas of the
      A0H to BFH page address range that are used by the display device.

  ■   The EMM device (part of V86MMGR), which wants to use a region of VM
      V86 address space between pages A0H and 100H for the high memory EMM
      3.20 Mapping Window.

  ■   The device responsible for management of the EBIOS page, page 9FH, on
      machines like the IBM PS/2(R) Model 80.


The following calls enable VxDs to allocate VM V86 address ranges for such
purposes and cooperate with other VxDs that also might want to use them.
There are two types of assignment that can be used: global, which applies to
all VMs in the system, and local, which applies to only one VM. The VDD
video and EBIOS page assignments are examples of global assignment (although
these could be local depending on the specifics of the implementation). The
EMM assignments are an example of local assignments. The EMM driver does not
want to take over VM V86 page assignment in VMs that are not using EMM
because then all those pages cannot be used by any other device. Thus, it
waits until a specific VM makes an EMM call of a certain type at which point
the EMM driver may do a local page assignment in that particular VM to
assign the EMM pages of the V86 address space to the EMM device. The global
versus local assignment is specified via the VMHandle parameter on the
calls. If the handle is nonzero, it is local; if the handle is zero, it is
global.

No protection is provided with this mechanism; all that is provided is
information so that devices can cooperate. There is nothing to prevent a VxD
from mapping pages that it does not own or a page owned by some other VxD. A
device that does these things is simply uncooperative and not correctly
implemented.

────────────────────────────────────────────────────────────────────────────
NOTE

All of these calls use the USE32 C calling convention. The true name of the
procedure has an underscore in front (i.e., Assign_Device _V86_Pages is
actually _Assign_Device_V86_Pages), and the arguments are pushed right to
left (unlike the PL/M calling convention used by Windows, which is left to
right). The return value(s) is returned in C standard EDX:EAX. It is the
responsibility of the caller to clear the arguments off the stack. Registers
EAX, ECX, and EDX are changed by calls. Registers DS, ES,
FS, GS, EBP, EDI, ESI, and EBX are preserved.

────────────────────────────────────────────────────────────────────────────

Assign_Device_V86_Pages Assign_Device_V86_Pages service
────────────────────────────────────────────────────────────────────────────

  unsigned Assign_Device_V86_Pages(VMLinrPage,nPages,VMHandle,flags)
  unsigned VMLinrPage;
  unsigned nPages;
  unsigned VMHandle;
  unsigned flags;

This call is used to assign a region of VM V86 address space to a device.
VMLinrPage specifies the linear page number (>=0, <=10Fh) of the first page
of V86 address space to be assigned. nPages specifies the number of pages to
be assigned starting at VMLinrPage. The entire specified range must be >=0,
<=10Fh, an error will occur if it is not. All of the specified pages must be
un-assigned, or an error will occur. VMHandle specifies the VM to Local
assign the pages in, if this parameter is 0, it means the pages are to be
Global assigned. There are currently no bits defined in the flags, this
parameter must be set to 0.


Return Value

Returns nonzero if the assignment was successful, returns zero if the
assignment failed (at least one page in the specified range is already
assigned, or invalid page range).


Comments

During device initialization only Global Assignments are allowed, and there
are restrictions on the pages which can be assigned. Pages between
FirstV86Page and page 0A0h can only be top down, in order assigned during
device initialization. Local Assignments, and General assignment between
FirstV86Page and page 0A0h must wait until device initialization is
complete.

Note that Global Assignment of a page that is already assigned, either Local
to any VM, or Global assigned will fail. Global assignment can only work on
pages which are not currently assigned in any VM.


Deassign_Device_V86_Pages
────────────────────────────────────────────────────────────────────────────

  unsigned DeAssign_Device_V86_Pages(VMLinrPage,nPages,VMHandle,flags)
  unsigned VMLinrPage;
  unsigned nPages;
  unsigned VMHandle;
  unsigned flags;

This call is used to deassign a region of VM V86 address which was
previously assigned with Assign_Device_V86_Pages. VMLinrPage specifies the
linear page number (>=0, <=10Fh) of the first page to be deassigned. nPages
specifies the number of pages to be deassigned starting at VMLinrPage. The
entire specified range must be >=0, <=10Fh, an error will occur if it is
not. All of the specified pages must be assigned, or an error will occur.
VMHandle specifies the VM to Local deassign the pages in, if this parameter
is 0, it means the pages are to be Global deassigned. There are currently no
bits defined in the flags, this parameter must be set to 0.


Return Value

Returns nonzero if the deassignment was successful, returns zero if the
deassignment failed (at least one page in the specified range is already
deassigned, or invalid page range).


Comments

During device initialization this call will always fail. This call only
works after device initialization is complete.

An extreme amount of chaos will occur if someone Global DeAssigns a range
which is actually Local Assigned, or DeAssigns a region which was not
obtained via a successful Assign_Device_V86_Pages.


Get_Device_V86_Pages_Array
────────────────────────────────────────────────────────────────────────────

  unsigned Get_Device_V86_Pages_Array(VMHandle,ArrayBufPTR,flags)
  unsigned VMHandle;
  unsigned ArrayBufPTR;
  unsigned flags;

This call is used to obtain a copy of the assignment bit map array for
Device_V86_Pages. This allows the caller to determine which regions of the
VM V86 address space are currently assigned, and which are available.
VMHandle specifies the VM to get the assignment bit map of, if this
parameter is 0, it means to get the Global assignment array. ArrayBufPTR
points to a buffer large enough to contain the array. The assignment array
is an array of 110h bits, one bit for each page in the range 0-10Fh. Thus
the size of the array is ((110h/8)+3)/4 = 9 DWORDS.

Bits in the array which are set (=1) indicate pages which are assigned, bits
which are clear (=0) indicate pages which are not assigned. Thus to test the
bit for page number N (0 N 10Fh) you could use code like this:

  mov ebx, N MOD 32   ; Bit number in DWORD
    mov eax, N / 32  ; DWORD index into array
    bt dword ptr ArrayBufPTR[eax*4],ebx ; Test bit for page N
    jnc short PageUnAssigned      PageAssigned:

Note that this code is mearly intended to illustrate how the bit array
works. This code is not the most efficient, or the only way to implement
this test. There are currently no bits defined in the flags, this parameter
must be set to 0.


Return Value

Returns nonzero if successful, returns zero if the bit array could not be
returned (Invalid VMHandle).


Comments

The Global Bit Array only indicates those pages which are currently Globally
owned. Bits with 0 in them do not necessarily indicate pages which can be
Global Assign_Device_V86_Paged. The reason is that one of the VMs in the
system may have that page Local Assign_Device_V86_Paged. In order to
determine if a page can be globally assigned, the Global array must be
examined, AND all of the VM Local arrays must be examined.


Hook_V86_Page
────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────
NOTE

This service does not use the C-calling Convention

────────────────────────────────────────────────────────────────────────────


Description

This service allows VxDs to intercept page faults in portions of the V86
address space of every virtual machine. It is used by devices such as the
Virtual Display Device to detect when particular address ranges are
accessed.

You must specify a page number and address of a call-back routine to this
service. If it is installed successfully, your "hook" will be called every
time a page fault occurs in ANY VM on that page. See the memory manager
_Modify_Pages documentation for making hooked pages not present and for
registering ownership of pages.

The callback routine is responsible for mapping an memory at the location of
the page fault or crashing the VM. In unusual circumstances it may be
appropriate to map a null page at the faulting faulting address page. See
the memory manager documentation for details on mapping memory and mapping
null pages.

────────────────────────────────────────────────────────────────────────────
NOTE

Do not rely on the contents of the CR2 (page fault) register. Use the value
passed to your call-back in EAX.
────────────────────────────────────────────────────────────────────────────


Entry

EAX = Page number (A0h - 0FFh) ESI = Address of trap routine


Exit

If carry flag set then ERROR: Invalid page number or page already hooked
else Page hooked


Uses

Flags


Callback

EAX = Faulting page number EBX = Current VM handle EBP does NOT point to the
client register structure.


19.3  GDT/LDT Management
────────────────────────────────────────────────────────────────────────────

These services provide a way for VxDs to allocate Global Descriptor Table
(GDT) selectors and set up a Local Descriptor Table (LDT) for protected-mode
execution. Notice that the intent of these services is to support segmented
environments in protected mode. In general, VxDs should never need to
allocate GDT selectors or set up an LDT. The only reason these services are
needed is to support protected-mode applications. Notice that the LDT is a
per-VM object; each VM may have its own LDT. Since enhanced Windows is a
flat model system, do not create multiple segments.


Allocate_GDT_Selector
────────────────────────────────────────────────────────────────────────────

  unsigned Allocate_GDT_Selector(DescDWORD1,DescDWORD2,flags)
  unsigned DescDWORD1;
  unsigned DescDWORD2;
  unsigned flags;

This call is used to create a new GDT selector. DescDWORD1 and DescDWORD2
form the 8 bytes of information to be placed in the new descriptor.
DescDWORD1 is the high order 4 bytes of the descriptor containing the high
16 bits of the base, the high 4 bits of the limit and the status and type
bits. DescDWORD2 is the low order 4 bytes for the descriptor containing the
low 16 bits of the base and limit. Use BuildDescDWORDs to help you set up
these arguments. There are currently no bits defined in the flags, this
parameter must be set to 0.


Return Value

Returns a 64 bit long which is actually two 32 bit DWORDs. The low DWORD
(EAX) is the non-zero selector if succesfull. The high DWORD (EDX) is split
into two 16 bit word returns. The low 16 bits of EDX is the GDT descriptor
which describes the GDT itself. Unlike the LDT, it is strongly recommended
that this selector not be used to edit the GDT. If you mess up editing the
LDT, you will probably just crash one app, but if you mess up editing the
GDT, you will crash the whole system. The high 16 bits of EDX is the number
of selectors currently in the GDT (the "limit" of the GDT expressed as a
number of selectors, (LIMIT+1)/8). Both DWORDS have value 0 if the
allocation failed (Bad DescDWORD arguments, GDT is full, insufficient memory
to grow GDT).


Comments

The RPL of the selector returned from this call will be set to the DPL of
the selector set in DescDWORD1.

The low 16 bits of the EDX return does not change, but it is safest to save
the value of the GDT selector after each Allocate_GDT_Selector call. This
selector will have DPL = RPL = 0, and the TI bit (bit 2) will be clear.

The high 16 bits of the EDX return must be saved after each call, if its
value is important, because the size of the GDT may change on each call.

The prefered method of changing a GDT descriptor is to use SetDescriptor,
rather than using the GDT selector which is returned by this call.


Allocate_LDT_Selector
────────────────────────────────────────────────────────────────────────────

  unsigned long
  Allocate_LDT_Selector(VMHandle,DescDWORD1,DescDWORD2,Count,flags)
  unsigned VMHandle;
  unsigned DescDWORD1;
  unsigned DescDWORD2;
  unsigned Count;
  unsigned flags;

This call is used to create new LDT selector(s) in the specified VM context.
VMHandle is a valid VM handle and indicates the VM context for which the
selector(s) will be valid. DescDWORD1 and DescDWORD2 form the 8 bytes of
information to be placed in the new descriptor(s). DescDWORD1 is the high
order 4 bytes of the descriptor containing the high 16 bits of the base, the
high 4 bits of the limit and the status and type bits. DescDWORD2 is the low
order 4 bytes for the descriptor containing the low 16 bits of the base and
limit. Use BuildDescDWORDs to help you set up these arguments. The Count
parameter specifies the number of contiguous LDT selectors to allocate. This
parameter supports Block Selector Assignment strategies. USE16 segmented
applications cannot address objects larger than 64K Bytes in size without
having multiple selectors that describe the sequential 64K Byte blocks of
the object. For an object <=64K bytes in size, or instances where it is
inappropriate, Count = 1. For an object >64K bytes in size, Count = (Size +
(64K - 1))/64K. Notice that the selectors allocated for count >1 all have
the same descriptor DWORDs in them. It is up to the caller to edit the base
and limits of the individual selectors in a Block Selector Assignment using
the LDT selector returned in the low 16 bits of EDX. There are currently no
bits defined in the flags, this parameter must be set to 0.


Return Value

Returns a 64 bit long which is actually two 32 bit DWORDs. The low DWORD
(EAX) is the nonzero selector if successful, if Count was >1, this is the
FIRST selector, the second is EAX+8, the third EAX+16, etc. The high DWORD
(EDX) is split into two 16 bit word returns. The low 16 bits of EDX is the
LDT descriptor which describes the LDT itself. The allows the caller to do
things such as change the present bit of LDT selectors and change the base
and limit. The high 16 bits of EDX is the number of selectors currently in
the LDT (the "limit" of the LDT expressed as a number of selectors,
(LIMIT+1)/8). Both DWORDS have value 0 if the allocation failed (Bad
DescDWORD arguments, LDT is full, invalid VMHandle insufficient memory to
grow LDT).


Comments

The RPL of the selector returned from this call will be set to the DPL of
the selector set in DescDWORD1 and the TI bit (bit 2) will be set.

The high 16 bits of the EAX return are zero since selectors are 16 bit
quantities.

Note that LDT selectors are PER VM and only valid in that VM context (VM
must be current VM for selector to be valid). Use SelectorMapFlat to look at
regions described by LDT selectors in VMs which are not the current VM.

The low 16 bits of the EDX return does not change once the LDT of a
particular VM is created, but it is safest to save the value of the LDT
selector after each  Allocate_LDT_Selector call. This selector will have DPL
= RPL = Protected Mode Application Privilege, and the TI bit (bit 2) will be
set.

The high 16 bits of the EDX return must be saved after each call, if its
value is important, because the size of the LDT may change on each call.

The multiple selectors allocated with Count >1 must be individually freed.
_Free_LDT_Selector does not have a count.

The prefered method of changing an LDT descriptor is to use SetDescriptor.

Use of ALDTSpecSel is not advised. Reliance on specific "hard coded" LDT
selectors is contrary to good system design principals. Note that a bit like
this does not exist for Allocate_GDT_Selector, this is intentional. A call
with this bit set may always fail for some values of the Count parameter,
and it may start failing for all values of the Count parameter in a later
release of the product.


BuildDescDWORDs
────────────────────────────────────────────────────────────────────────────

  unsigned  long BuildDescDWORDs(DESCBase,DESCLimit,DESCType,DESCSize,flags)
  unsigned  DESCBase;
  unsigned  DESCLimit;
  unsigned  DESCType;
  unsigned  DESCSize;
  unsigned Flags

This call is used to help you build the DescDWORD1 and DescDWORD2 arguments
for calls to Allocate_LDT/GDT_Selector. DESCBase is the 32 bit BASE for the
descriptor. DESCLimit is the 20 bit LIMIT for the descriptor. DESCType
specifies the type BYTE (Only low 8 bits of the parameter are valid, other
bits must be 0) for the descriptor. This is the byte that occupies bits 8-15
of the high DWORD of the descriptor (Present bit, DPL and TYPE fields).
DESCSize specifies bits 20-23 of the high DWORD of the descriptor
(Granularity, Big/Default). Notice that these bits occupy bits 4-7 of the
DESCSize parameter, other bits must be 0. In other words DESCSize specifies
a byte just like DESCType where only the high 4 bits of the byte are
specified.

Current flags bits:

  BDDExplicitDPL EQU 00000000000000000000000000000001B

All unused bits must be zero. BDDExplicitDPL, if set, indicates that the DPL
value specified in the DESCType field is to be used. If this bit is clear,
then the DPL specified in the DESCType field is ignored and the DPL returned
will be set to the protected mode application RPL. Since most selectors are
built for the use by protected mode applications, this provides a
convienient way to build descriptors without having to actually know which
ring protected mode applications run in.


Return Value

Returns the low DWORD of the descriptor (DescDWORD2) in EAX, and the high
DWORD of the descriptor (DescDWORD1) in EDX.


Comments

If you are building selectors for use by Protected Mode applications use the
built-in capability provided by not setting the BDDExplicitDPL bit. Do not
make assumptions about which ring protected mode applications run in. The
selection of a ring for PM applications will be changed in future revs of
Windows.


Free_GDT_Selector
────────────────────────────────────────────────────────────────────────────

  unsigned Free_GDT_Selector(Selector,flags)
  unsigned Selector;
  unsigned flags;

This call is used to free a GDT selector allocated with a previous
Allocate_GDT_Selector call. Selector is the return from a previous
Allocate_GDT_Selector call. There are currently no bits defined in the
flags, this parameter must be set to 0.


Return Value

Returns nonzero value if successful, returns zero if the free failed
(invalid Selector).


Comments

Certain system selectors cannot be freed since they are required for
operation of enhanced Windows.


Free_LDT_Selector
────────────────────────────────────────────────────────────────────────────

  unsigned Free_LDT_Selector(VMHandle,Selector,flags)
  unsigned VMHandle;
  unsigned Selector;
  unsigned flags;

This call is used to free a LDT selector allocated with a previous
Allocate_LDT_Selector call. VMHandle indicates the VM context of the
selector. Selector is the return from a previous Allocate_LDT_Selector call.
There are currently no bits defined in the flags, this parameter must be set
to 0.


Return Value

Returns nonzero value if successful, returns zero if the free failed
(invalid Selector, invalid VMHandle).


Comments

The RPL bits of the passed Selector are ignored by this call.


GetDescriptor
────────────────────────────────────────────────────────────────────────────

  unsigned long GetDescriptor(Selector,VMHandle,flags) unsigned Selector;
  unsigned VMHandle;
  unsigned flags;

This call is used to get a copy of the two descriptor DWORDs associated with
the given LDT or GDT Selector. Selector is a GDT or LDT selector value to
get the descriptor of. The VMHandle parameter is ignored if Selector is a
GDT selector. If Selector is an LDT selector, then VMHandle indicates the
appropriate VM context for the Selector. There are currently no bits defined
in the flags, this parameter must be set to 0.


Return Value

Returns the low DWORD of the descriptor (DescDWORD2) in EAX, and the high
DWORD of the descriptor (DescDWORD1) in EDX. Returns zero in both DWORDs if
there was an error (invalid selector, invalid VM handle).


Comments

The high 16 bits of the Selector argument are ignored (this is because the
80386 CPU often sets them to somewhat random values when DWORD operations
are performed on segment registers).

The RPL bits of Selector are ignored.

The VMHandle parameter must be valid for LDT selectors.


SetDescriptor
────────────────────────────────────────────────────────────────────────────

  unsigned SetDescriptor(Selector,VMHandle,DescDWORD1,DescDWORD2,flags)
unsigned Selector;
  unsigned VMHandle;
  unsigned DescDWORD1;
  unsigned DescDWORD2;
  unsigned flags;

This call is used to set (change) the descriptor of the given Selector.
Selector is a GDT or LDT selector value to set the descriptor of. The
VMHandle parameter is ignored if Selector is a GDT selector. If Selector is
an LDT selector, then VMHandle indicates the appropriate VM context for the
Selector. DescDWORD1 and DescDWORD2 form the 8 bytes of information to be
placed in the descriptor. DescDWORD1 is the high ORDER 4 bytes of the
descriptor containing the high 16 bits of the base, the high 4 bits of the
limit and the status and type bits. DescDWORD2 is the low ORDER 4 bytes for
the descriptor containing the low 16 bits of the base and limit. Use
BuildDescriptorDWORDs to help you set up these arguments. There are
currently no bits defined in the flags, this parameter must be set to 0.


Return Value

Returns non-zero value if succesfull, returns zero if it failed (invalid
Selector, invalid VMHandle).


Comments

The high 16 bits of the Selector argument are ignored (this is because the
80386 CPU often sets them to somewhat random values when DWORD operations
are performed on segment registers).

The RPL bits of Selector are ignored.

The VMHandle parameter must be valid for LDT selectors.


19.4  System Heap Allocator
────────────────────────────────────────────────────────────────────────────

The purpose of the heap allocator is to provide a memory manager service to
system components to allocate small (i.e., less than a page size) blocks of
memory for long term or short term use.

────────────────────────────────────────────────────────────────────────────
NOTE

All of these calls use the USE32 C calling convention. The true name of the
procedure has an underscore in front (i.e., HeapAllocate is actually
_HeapAllocate), and the arguments are pushed right to left (unlike the PL/M
calling convention used by Windows, which is left to right). The return
value(s) is returned in C standard EDX:EAX. It is the responsibility of the
callerto clear the arguments off the stack. Registers EAX, ECX, and EDX are
changed by calls. Registers DS, ES, FS, GS, EBP, EDI
, ESI, and EBX are preserved.
────────────────────────────────────────────────────────────────────────────
The heap uses a boundary tag allocation scheme similar to the one used by
the MS-DOS operating system. This has the benefit of not placing some fixed
limit on the total number of heap blocks. It has the disadvantage of having
a fixed overhead of extra space per block. The heap overhead is about 16
bytes per block. Users should keep this in mind when allocating lots of
objects of small size. Try to combine such needs into larger heap blocks to
cut down on the overhead.

────────────────────────────────────────────────────────────────────────────
WARNING

You are strongly warned against making assumptions about the placement and
size of the heap boundary tag structures. Future versions of enhanced
Windows may change this behavior of the heap.
────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────
NOTE

4 byte (DWORD) alignment is maintained on heap blocks. This could be
increased in a later version, but at least DWORD alignment is guaranteed.
────────────────────────────────────────────────────────────────────────────


HeapAllocate
────────────────────────────────────────────────────────────────────────────

  unsigned HeapAllocate(nbytes,flags)
     unsigned nbytes;
     unsigned flags;

This is the call to allocate a block from the heap. nbytes is a 32 bit
unsigned integer which is the size, in bytes, of the block. Current flags
bits:

  HeapZeroInit EQU  00000000000000000000000000000001B

All unused bits must be zero. HeapZeroInit, if set, indicates that if the
allocation is succesful, the memory is to be initialized with value 0 in all
bytes of the block. If HeapZeroInit is clear, the block will have completely
random values in it.


Return Value

The return value is the 32 bit RING 0 address (offset relative to standard
enhanced Windows RING 0 DS) of the block. Value is 0 if the allocation
failed (insufficient memory).


Comments

Blocks are DWORD aligned as noted, but sizes do not have to be a multiple of
4.

There is no "protection" of the heap. Care must be taken not to overrun the
size of your block. Failure to do this will result in odd behavior and
crashes.

There is no "motion" of blocks in the heap (heap blocks are all fixed),
except via  HeapReAllocate, and therefore no compaction. You are advised not
to use the heap in such a way as to severely fragment it. You will end up
wasting lots of memory by doing this.

The Flag bit equates are defined by including VMM.INC, please use the
equates.

Allocation of 0 length heap blocks is not allowed.


HeapFree
────────────────────────────────────────────────────────────────────────────

  unsigned HeapFree(hAddress,flags)
     unsigned hAddress;
     unsigned flags;

This call is used to free an existing block of heap. hAddress is the value
returned from a previous call to HeapAllocate or HeapReAllocate and
indicates the block to be freed. There are currently no bits defined in the
flags, this parameter must be set to 0.


Return Value

Returns nonzero value if the block was succesfully freed, zero if the free
was unsuccesful (invalid hAddress).


HeapGetSize
────────────────────────────────────────────────────────────────────────────

  unsigned HeapGetSize(hAddress,flags)
     unsigned hAddress;
     unsigned flags;

This call is used to get the size of an existing block of heap. hAddress is
the value returned from a previous call to HeapAllocate or HeapReAllocate
and indicates the block to get the size of. There are currently no bits
defined in the flags, this parameter must be set to 0.


Return Value

Returns the size, in bytes, of the block. Returns zero if there was an error
(invalid hAddress).


HeapReAllocate
────────────────────────────────────────────────────────────────────────────

  unsigned HeapReAllocate(hAddress,nbytes,flags)
     unsigned hAddress;
     unsigned nbytes;
     unsigned flags;

This call is used to grow or shrink or reinitialize an existing block of
heap. hAddress is the value returned from a previous HeapAllocate or
HeapReAllocate call and indicates the block to be reallocated. nbytes is a
32 bit unsigned integer which is the new size in bytes of the block. Current
flags bits:

  HeapZeroInit  EQU 00000000000000000000000000000001B HeapZeroReInit  EQU
00000000000000000000000000000010B HeapNoCopy  EQU
00000000000000000000000000000100B

All unused bits must be zero. HeapZeroInit, if set, indicates that if the
reallocation is succesful, and the reallocation is growing the size of the
block, the "grow area" of the block is to be initialized with value 0 in all
bytes. This bit is ignored on a reallocation which is not growing the size
of the block. HeapZeroReInit, if set, indicates that the ENTIRE block is to
be reinitialized with value zero in all bytes of the block. HeapNoCopy, if
set, indicates that the previous contents of the block are irrelevant, and
don't need to be copied into the newly sized block. There is no reason that
more than one of these bits should be set. If none of the bits are set, the
previous contents of the block are copied into the new block, up to the
lesser of the size of the new block, and the size of the old block, and the
"grow area", if any, is not initialized with anything.


Return Value

The return value is the 32 bit RING 0 address (offset relative to standard
enhanced Windows RING 0 DS) of the new block. Value is 0 if the reallocation
failed (insufficient memory, or invalid hAddress).


Comments

Do not make assumptions about the relationship between the passed in
hAddress and the hAddress returned. Assume that the returned hAddress is
always different than the passed in hAddress.

In the case where this call fails, the passed in hAddress block remains
valid. In the case where this call works and returns a new hAddress, the
passed in hAddress is no longer valid (old block has been HeapFreed).

There is no "protection" of the heap. Care must be taken not to overrun the
size of your block. Failure to do this will result in odd behavior and
crashes.

There is no "motion" of blocks in the heap (heap blocks are all fixed), and
therefore no compaction. You are advised not to use the heap in such a way
as to severely fragment it. You will end up wasting lots of memory by doing
this.

Note that this call can be used to reset the contents of an existing heap
block to 0 by setting nbytes to the current size of the block and setting
HeapZeroReInit.

You cannot HeapReAllocate a block to size 0, use HeapFree.

The Flag bit equates are defined by including VMM.INC, please use the
equates.


19.5  System Page Allocator
────────────────────────────────────────────────────────────────────────────

The page allocator is the set of services by which system memory is
allocated and mapped into a VM's linear address space. VxD's can also
examine and modify the status of mapped pages and lock or unlock pages into
physical memory. VxD's can also allocate pages for the VxD's exclusive use.
For example, the current state of the video memory can be saved for
clipboard operations.

────────────────────────────────────────────────────────────────────────────
NOTE

All of these calls use the USE32 C calling convention. The true name of the
procedure has an underscore in front (i.e., PageAllocate is actually
_PageAllocate), and the arguments are pushed right to left (unlike the PL/M
calling convention used by Windows, which is left to right). The return
value(s) is returned in C standard EDX:EAX. It is the responsibility of the
callerto clear the arguments off the stack. Registers EAX, ECX, and EDX are
changed by calls. Registers DS, ES, EBP, EDI, ESI, and
@AE@%EBX are preserved.
────────────────────────────────────────────────────────────────────────────


CopyPageTable
────────────────────────────────────────────────────────────────────────────

  unsigned CopyPageTable(LinPgNum,nPages,PageBufPTR,flags)
     unsigned LinPgNum;
     unsigned nPages;
     unsigned *PageBufPTR;
     unsigned flags;

This call is used to obtain a copy of an enhanced Windows page table. This
call is intended as an assist to enhanced Windows system components that
need to analyze the linear to physical mapping (such as DMA devices).
LinPgNum is the page number of the first page of the range. This can be
anything in the range 0 - 0FFFFFh. Thus addresses in the range 0-3FFh refer
to addresses in the 1M V86 address space of the current VM. To compute the
page number of any region simply take the address relative to the standard
enhanced Windows RING 0 DS and shift it right by 12 bits. For example, the
linear address 60001AB6h is in page number 60001h. Alignment considerations
of this address (beyond 4K alignment) are the responsibility of the caller.
nPages is the number of page table entries to copy. PageBufPTR is a 32 bit
RING 0 offset relative to the standard RING 0 DS which is the address of a
buffer where the page table will be copied. Caller must insure that this
buffer is large enough. Each page table entry is a DWORD, so the buffer must
be at least nPages*4 bytes long. There are currently no bits defined in the
flags, this parameter must be set to 0.


Return Value

Returns a nonzero value if the copy is succesful, returns 0 value if the
copy was succesful, but at least a part of the range overlapped a region
where the corresponding page directory entry is not present.


Comments

You get a copy of the page table; writing to your buffer has no effect.

Note that V86 page tables stop at page 10Fh.

To look at the page table of a VM that is not the current VM simply use the
high linear address of the VM. For instance, to look at the page table
starting at V86 address 0A000:0 of a VM which is not the current VM:

  mov eax,0A0000h   ; V86 linear adress of 0A000:0
  add eax,[ebx.CB_High_Linear] ; High linear address
  shr eax,12   ; Convert to page number

Note that the above sequence always works correctly (works if the VM is the
current VM as well). So simply doing this in all cases avoids the
complication of worrying about whether the VM is the current VM.

The intent of this call is for you to look at the physical addresses in the
high 20 bits of the entries. The low 12 bits of system information may be
examined however.

You are warned to be careful about keeping this buffer for any length of
time. The actual page table entries can change while the copy you have
won't. The information in the copy should be analyzed quickly.


GetDemandPageInfo
────────────────────────────────────────────────────────────────────────────

  void GetDemandPageInfo(BufPtr,flags)
     DemandInfoStruc *BufPtr;
     unsigned flags;

This call is for use by the demand paging device. It provides information
for the demand pager.

  DemandInfoStruc struc
    DILin_Total_Count  dd ?   ; Size of linear address space in pages
    DIPhys_Count   dd ?   ; Count of phys pages
    DIFree_Count   dd ?   ; Count of free phys pages
    DIUnlock_Count   dd ?   ; Count of unlocked phys Pages
    DILinear_Base_Addr  dd ?   ; Base of pageable address space
    DILin_Total_Free  dd ?   ; Total free linear pages
    DIReserved   dd 12 DUP(?) ; Reserved
  DemandInfoStruc ends

DILin_Total_Count is the size in pages of the linear address space subject
to demand paging. DILinear_Base_Addr is the linear address of the start of
the demand pageable region. Thus there are DILin_Total_Count pages starting
at address DILinear_Base_Addr which are subject to demand paging.
DILin_Total_Free is the number of the DILin_Total_Count pages which are
currently free. Notice that this space may not be allocatable in a single
block, it is the total free, not the size of the largest free block. Note
that if DILinear_Base_Addr == 0, this means that the demand pageable region
of the system is not contiguous. DIPhys_Count is the total number of
physical pages under the control of the memory manager. DIFree_Count is the
number of pages currently on the free list. DIUnlock_Count is the count of
pages which are currently unlocked, notice that free pages are unlocked.
There are currently no bits defined in the flags, this parameter must be set
to 0.


Return Value

This call does not have a return value. It simply fills in the structure
pointed to by BufPtr.


Comments

The reserved field is exactly that, reserved. Do not make any assumptions
about what is in this region. Behavior will change in later releases.


GetFreePageCount
────────────────────────────────────────────────────────────────────────────

  unsigned long (flags)
     unsigned flags;

This call is used to obtain the count of free 4K pages. And the count of
pages that can be allocated as PageLocked. There are currently no bits
defined in the flags, this parameter must be set to 0.


Return Value

The return value is a 64 bit long which is actually two 32 bit DWORDS. The
Low DWORD (EAX) is the 32 bit count of free 4K pages in the system which
could be allocated with the PageAllocate call. The High DWORD (EDX) is the
32 bit count of pages available for allocation as PageLocked pages at the
current time.


Comments

You should be careful about making assumptions about being able to turn
around and issue a call to allocate all of the pages returned by this call.
Besides any alignment considerations, it is possible someone could get in
and allocate some or all of the pages before you. This call is intended to
be advisory in nature.

Note that, in a demand paged virtual memory system such as enhanced Windows,
the free pages count is usually very close to 0. It is more relevant to use
the EDX return to make judgements about allocation possibility. EDX contains
the count of pages currently available for allocation as PageLocked pages.
Note that many assumptions are not valid. EAX<=EDX is not a valid assumption
for instance.

Note that in a virtual memory environment it is not a good idea to go
soaking up tons of virtual address space. Start with some, then
PageReAllocate it to make it bigger if needed.


GetSetPageOutCount
────────────────────────────────────────────────────────────────────────────

  unsigned GetSetPageOutCount(NewCount,flags)
     unsigned NewCount;
     unsigned flags;

This call is for use by the demand paging device. It allows the paging
device to manipulate a memory manager parameter associated with demand
paging. This parameter is the "page out ahead" count. Whenever a page is
paged out to satisfy a page in, an additional PageOutCount-1 pages are also
paged out and put on the free list (if possible).There is one bit in the
flags:

  GSPOC_F_Get  equ   00000000000000000000000000000001B

All other bits must be zero. If GSPOC_F_Get is set, the call returns the
current value of the page out count in EAX, and the NewCount parameter is
ignored. If GSPOC_F_Get is not set, the call sets the value of the page out
count to NewCount.


Return Value

Returns the page out count if GSPOC_F_Get is set, else it has no return.

────────────────────────────────────────────────────────────────────────────
WARNING

This call is intended for use by the PageSwap device, others should not be
calling it. Others making this call can disturb the operation of the
PageSwap device.
────────────────────────────────────────────────────────────────────────────


Comments

There is an equate for the flag bit in VMM.INC, use the equate.


GetSysPageCount
────────────────────────────────────────────────────────────────────────────

  unsigned GetSysPageCount(flags)
     unsigned flags;

This call is used to obtain the current count of system (PG_SYS) 4K pages.
There are currently no bits defined in the flags, and this parameter must be
set to 0.


Return Value

The return value is the 32 bit count of 4K pages allocated as PG_SYS pages
in the system.


Comments

 It is generally true that this number is the size of enhanced Windows.
However, this is the general case only.


GetVMPgCount
────────────────────────────────────────────────────────────────────────────

  unsigned long GetVMPgCount(VMHandle,flags)
     unsigned VMHandle;
     unsigned flags;

This call is used to get the current count of 4K pages allocated to a
particular VM. The VMHandle parameter must be a valid VM handle and
indicates the VM to get the allocated page count of. There are currently no
bits defined in the flags, this parameter must be set to 0.


Return Value

The return value is a 64 bit long which is actually two 32 bit DWORDS. The
Low DWORD (EAX) is the total count of pages (of all types but PG_SYS) in the
system allocated for this VM. The High DWORD (EDX) is the count of pages
which are allocated to this VM, but which are not mapped into the VM's 1Meg
address space at the current time. Value (both dwords) is 0 if the call
failed (invalid VMHandle).


Comments

You should be careful about assuming that EAX-EDX is the size of the VM. It
is in one sense, but not in the standard MS-DOS senses.


MapIntoV86
────────────────────────────────────────────────────────────────────────────

  unsigned MapIntoV86(hMem,VMHandle,VMLinPgNum,nPages,PageOff,flags)
     unsigned hMem;
     unsigned VMHandle;
     unsigned VMLinPgNum;
     unsigned nPages;
     unsigned PageOff;
     unsigned flags;

This call is used to map some or all of the pages of a memory block into a
specific VM's Virtual 8086 address space. hMem is the value returned from a
previous call to PageAllocate or PageReAllocate and indicates the block to
be mapped. VMHandle parameter must be a valid VM handle and indicates the VM
into which the map is to occur. VMLinPgNum is the address in the 1M V86
address space where the map will start (this is a page number, thus linear
address 60000h = page 60h). Alignment considerations of this address (beyond
4K alignment) are the responsibility of the caller. Map addresses below page
10h, or above 10Fh will cause an error. nPages is the number of pages to
map. PageOff is the number of pages into the hMem block to the first page of
the block which is to be mapped at VMLinPgNum (thus PageOff is 0 to map the
first page of hMem at VMLinPgNum). nPages and PageOff allow one hMem block
to be scatter mapped into different VM locations. An error will occur if
PageOff + nPages is greater than the size of hMem.

Current flags bits:

  PageDEBUGNulFault  EQU 00000000000000000000000000010000B

All unused bits must be zero. PageDEBUGNulFault, if set, indicates that if
hMem is the handle of the NUL system page, and this is the DEBUG version of
enhanced Windows, access to these pages should cause a page fault DEBUG
exception. This bit is ignored if hMem is not the system NUL page handle, or
this is not enhanced Windows DEBUG.

It is generally true that hMem blocks mapped with this call should not be
composed of PG_SYS pages. This is not disallowed, but is not advised.

There is a special hMem handle that can be used with this call. The value of
this handle is obtained by calling the routine GetNulPageHandle (actual name
_GetNulPageHandle) which will return you this special hMem handle in EAX.
This is the hMem of the system NUL page. This page is used to occupy regions
of the address space which are "unused" but for which it is not desirable to
cause a page fault if they are accessed. The NUL page is multiply mapped at
many locations in the system, so its contents are always random. Under
enhanced Windows DEBUG, a fault occurs if the NUL page is touched and the
PageDEBUGNulFault bit was set on the call which mapped the page.

If the PageSwap device is type one (not direct to hardware), there is an
implied PageLock on the pages mapped with this call, and an implied
PageUnlock on the pages which this call is mapping over. This is consistent
with the fact that pages mapped into V86 address space must be locked (V86
memory cannot be demand paged). If the PageSwap device is type two (direct
to hardware) than the implied lock and unlock done by this call are disabled
because in the case of a type two PageSwap device V86 memory CAN be demand
paged. See the PageAllocate documentation for a description of the different
PageSwap device types and their relevance.


Return Value

Returns a nonzero value if the map is succesful, returns 0 value if the map
was unsuccesful (invalid hMem, invalid VMHandle, map range illegal, size
discrepancy, insufficient memory on implied PageLock).


Comments

The implied PageLock, which is performed on all of the pages mapped if the
PageSwap device is type oneAg, is consistent with the fact that V86 memory
cannot be Demand Paged while the VM is in a runable state. Whenever the V86
memory mapping is changed via MapIntoV86, the previous memory that was
mapped in that region of the VM is unlocked. The correct way to think of
this is that there is an implied PageLock whenever memory is mapped into a
V86 context, and an implied PageUnlock whenever it is "unmapped" from the
V86 context. This "unmapping" can occur when: A different handle (including
the NulPageHandle) is MapIntoV86ed or LinMapIntoV86ed to the region, or a
PhysIntoV86 is performed to the region.

There is nothing to prevent you from mapping the same block, or piece of a
block, into multiple places in a VM, or into multiple VMs. Such operations
are not particularly advisable though. For one thing, the reporting of
memory owned by a VM will be disturbed. For this reason it is also not
generally a good idea to map pages that were allocated as belonging to one
VM into a different VM. The one exception to this general rule is the
request for a map by one VM to look at the memory of a different VM. Such
maps should be of a relatively short duration.

The page attributes for these pages will be P_USER+P_PRES+P_WRITE. P_DIRTY
and P_ACC will be cleared by the call. PG_TYPE will be set to whatever the
type of the hMem pages are.

The Flag bit equates are defined by including VMM.INC, please use the
equates.

The intent of MapIntoV86 support for pages between page 10h and FirstV86Page
is to support WIN386 devices which have Allocate_Global_V86_Data_Area a
GVDAPageAlign region. Use of mapping in this region to other addresses can
easily crash the system and should be avoided.

Regions which span across FirstV86Page are not allowed.

The reason for the page 10h limitation is that on most versions of the Intel
80386 CPU there is an errata which prevents you from setting up a Linear !=
Physical address mapping in the first 64K of the address space.


ModifyPageBits
────────────────────────────────────────────────────────────────────────────

  unsigned
ModifyPageBits(VMHandle,VMLinPgNum,nPages,bitAND,bitOR,pType,flags)
     unsigned VMHandle;
     unsigned VMLinPgNum;
     unsigned nPages;
     unsigned bitAND;
     unsigned bitOR;
     unsigned pType;
     unsigned flags;

This call is used to modify the page protection bits associated with
PG_HOOKED pages in the V86 address space of a VM. It allows the P_PRES,
P_WRITE, and P_USER bits of the pages to be modified along with PG_TYPE if
appropriate. The VMHandle parameter must be a valid VM Handle and indicates
the VM whose page bits are to be modified. VMLinPgNum is the page number in
the 1M V86 VM address space where the modification will start (this is a
page number, thus linear address A0000h = page A0h). When clearing the
P_PRES bit (making pages not present), all of the pages specified (nPages
starting at VMLinPgNum) must be PG_HOOKED pages for which a HOOK Page Fault
handler has been registered, and pType must be PG_HOOKED. nPages is the
number of pages to modify the bits of. Addresses below the start of VM
specific memory, or above 10Fh will cause an error. bitAND is an AND mask
for the bits, bitOR is an OR mask. Thus to clear P_PRES, P_WRITE, and
P_USER, bitAND would be (not P_PRES+P_WRITE+P_USER), and bitOR would be
zero. To set P_USER, and clear P_WRITE, leaving P_PRES unchanged, bitAND
would be (NOT P_WRITE), and bitOR would be P_USER. Having bits other than
P_WRITE, and P_USER set in bitOR will cause an error. Having bits other than
P_PRES, P_WRITE, and P_USER clear in bitAND will cause an error.

This call always has the side effect of clearing P_DIRTY and P_ACC. Thus to
just clear these two bits, give a bitAND of 0FFFFFFFFh, and a bitOR of 0.
pType indicates a value to be placed in the PG_TYPE field. The allowed
values are:

  PG_HOOKED   EQU 7
  PG_IGNORE   EQU -1 (0FFFFFFFFh)

Any other value will cause an error. PG_IGNORE indicates that the PG_TYPE
field is not to be modified by the call. This is the value that must be set
if P_PRES bit is being set (or being left set). PG_HOOKED must be specified
if the P_PRES bit is being cleared by the call. Recall that making a
PhysIntoVM call sets the type field for the physical pages to PG_SYS. This
parameter is provided so that the page types can be reset to PG_HOOKED when
the mapping is changed to not present. Recall that MapIntoVM also resets the
PG_TYPE field to the type of the pages of hMem. There are currently no bits
defined in the flags, this parameter must be set to 0.


Return Value

Returns a nonzero value if successful, returns 0 value if unsuccessful
(invalid VMHandle, invalid bits in bitAND or bitOR, invalid pType, page
range bad).


Comments

You cannot use this call to set the Present bit. You may either clear the
present bit, or leave it unaffected. Use MapIntoV86 or PhysIntoV86 to make
pages present.


PageAllocate
────────────────────────────────────────────────────────────────────────────

  unsigned PageAllocate(nPages,pType,VMHandle,AlignMask,minPhys,
maxPhys,PhysAddrPTR,flags)
    unsigned nPages;
    unsigned pType;
    unsigned VMHandle;
    unsigned AlignMask;
    unsigned minPhys;
    unsigned maxPhys;
    unsigned *PhysAddrPTR;
    unsigned flags;

This is the call to allocate a block of memory. The memory allocated is
actually just linear address space, whether there is actually physical
memory mapped for this block as part of the allocation is specified by the
flags. nPages is a 32 bit unsigned integer which is the size in 4K pages of
the block. pType indicates the type of page(s) being allocated:

  PG_VM  EQU  0
  PG_SYS  EQU  1
  PG_HOOKED EQU  7

PG_VM pages are pages which are specific to a particular VM context. The
handle of PG_VM memory blocks will typically be placed in the VM Control
Block someplace. PG_HOOKED pages are pages which will be mapped into the VM
at locations where the component has registered a HookPageFault handler.
Like PG_VM pages, PG_HOOKED pages are specific to a particular VM context.
The VMHandle parameter must be a valid VM Handle for all page types except
PG_SYS. PG_SYS pages are global system pages which are valid in all VM
contexts (pages are specific to the enhanced Windows system component which
allocates them, rather than to a VM). The VMHandle parameter is not relevant
to PG_SYS pages and it must be set to 0 when allocating PG_SYS pages.

Current flags bits:

  PageZeroInit  EQU 00000000000000000000000000000001B
  PageUseAlign  EQU 00000000000000000000000000000010B
  PageContig  EQU 00000000000000000000000000000100B
  PageFixed  EQU 00000000000000000000000000001000B
  PageLocked  EQU 00000000000000000000000010000000B PageLockedIfDP  EQU
00000000000000000000000100000000B

All unused bits must be zero. PageLocked, if set, indicates that a PageLock
is implied as part of the PageAllocate operation. This forces the allocate
to make all pages of the handle present when the handle is allocated
consistent with the implied PageLock. PageLockedIfDP, if set, indicates that
a PageLock is implied as part of the PageAllocate only if the PageSwap
device is not direct to hardware. There are two basic behavior types for the
PageSwap device. Type one pages through MS-DOS and/or the ROM BIOS. This
type of PageSwap device places restrictions on the ability to demand page
certain types of system memory because of the fact that it runs partly in
V86 mode as part of its operation. PageSwap type two pages by talking
directly to the disk hardware. This second type of PageSwap device removes
some of the restrictions because it runs completely in protected mode when
accessing the paging device. PageLocked indicates that the memory should be
locked regardless of which type of PageSwap device is present.
PageLockedIfDP indicates that this memory only needs to be locked if the
PageSwap device is type one. PageFixed, if set, indicates behavior similar
to PageLocked as far as the implied PageLock is concerned, and in addition a
Fixed handle can never be unlocked, and its linear address will never change
(via PageReAllocate). Note that ReAllocation of a Fixed handle will
generally not succeed due to the Fixed restriction on the ability to change
the linear address of the handle. Note that an allocation without an implied
PageLock via PageLocked, PageLockedIfDP, or PageFixed will simply allocate
linear address space. The pages of such a handle will be made present "on
demand" when the address space is touched. If it is desired to make part of
the handle present to perform some function, use PageLock to force the
contents to be loaded. PageUseAlign, if set, indicates that the AlignMask,
minPhys, maxPhys, and PhysAddrPTR parameters are specified. If PageUseAlign
is clear, the AlignMask, minPhys, maxPhys, and PhysAddrPTR parameters are
set to 0 and ignored. Note that if PageUseAlign is set, PageFixed must also
be specified. It makes no sense to have an aligned memory handle which is
not fixed. PageZeroInit, if set, indicates that if the allocation is
succesful, the memory is to be initialized with value 0 in all bytes of the
block. If PageZeroInit is clear, the block will have completely random
values in it. PageContig, if set, indicates that the Physical memory pages
of the block are to occupy sequential Physical memory addresses (memory is
"physically contiguous"). PageContig is ignored if PageUseAlign is not set.


PageUseAlign is provided to assist device drivers that wish to allocate
buffers for use by the device which have additional alignment restrictions
enforced by the hardware (such as 64K and 128K alignment for DMA). If the
PageUseAlign bit is set, AlignMask specifies an alignment (power of 2> 4k)
requirement for the first physical page of the block. Physical page numbers
are the physical address of the page shifted right by 12. Correct alignment
is tested for by ANDing AlignMask with the first physical page number and
testing for zero. If the AND is zero, the page has the correct alignment.
Thus:

  00000000h    =       4K alignment (ignore AlignMask)
  00000001h    =       8K alignment
  00000003h    =      16K alignment
  00000007h    =      32K alignment
  0000000Fh    =      64K alignment
  0000001Fh    =     128K alignment

Remember that you will probably also want to set the PageContig bit. minPhys
and maxPhys place additional physical address restrictions on the physical
pages of the memory block. These specify the minimum and maximum allowed
physical page numbers. All physical page numbers of the block must be
>=minPhys, and <maxPhys. For instance, for setting up a DMA buffer for an
80386 accelerator card in a PC XT, the buffer needs to be physically
restricted to pages less than 1 MB since the XT DMA controller cannot DMA
into pages above 1 MB. In this case, minPhys would be 0, and maxPhys would
be 100h.  If you don't want to specify this (i.e. you just want AlignMask),
set minPhys to 0, and maxPhys to 0FFFFFFFFh. Note that when PageUseAlign is
set, the physical page address (physical page number shifted left by 12) of
the start of the block will be returned via the PhysAddrPTR pointer
parameter.

────────────────────────────────────────────────────────────────────────────
NOTE

PageUseAlign PageAllocations can only be performed during device
initialization. Aligned PageAllocations will fail if done after device
initialization.

────────────────────────────────────────────────────────────────────────────


Return Value

The return value is a 64 bit long which is actually two 32 bit DWORDS. The
Low DWORD (EAX) is the memory handle of the block. The High DWORD (EDX) is
the 32 bit RING 0 address (offset relative to standard enhanced Windows Ring
0 DS) of the block. If PageUseAlign was specified, the physical address of
the start of the block is placed in the DWORD pointed to by PhysAddrPTR.
Value (both DWORDs) is 0 if the allocation failed (insufficient memory).


Comments

You should be careful about making assumptions about any apparent
relationship between the memory handle and the blocks RING 0 or physical
address. Any such apparent relationship is subject to change in a later
release.

PhysAddrPTR had better point somewhere reasonable when PageUseAlign is
specified. There is no way to check its validity, if it's garbage you'll
either cause a page fault or stomp on something you shouldn't.

PageAllocation of 0 length blocks is not allowed.

PageLocked and PageLockedIfDP should not both be set. Only one, or the
other, or neither are valid settings. Note also that PageLockedIfDP cannot
be set on calls made before the init complete system control call is made.
This is because it is not possible to ask the PageSwap device what type it
is before it has been initialized.

The Flag bit equates are defined by including VMM.INC, please use the
equates.


PageFree
────────────────────────────────────────────────────────────────────────────

  unsigned PageFree(hMem,flags)
     unsigned hMem;
     unsigned flags;

This call is used to free an existing block of pages. hMem is the value
returned from a previous call to PageAllocate or PageReAllocate and
indicates the block to be freed. There are currently no bits defined in the
flags, this parameter must be set to 0.


Return Value

Returns nonzero value if the block was succesfully freed, zero if the free
was unsuccesful (invalid hMem).


Comments

It is the responsibility of the enhanced Windows system components which
allocate non-PG_SYS pages to free them when the VM they are associated with
is destroyed. There is no "automatic" freeing of such memory done by the
memory manager. PG_SYS pages do not need to be freed before enhanced Windows
exits.

It is not an error to PageFree a handle which is all or partially locked.

────────────────────────────────────────────────────────────────────────────
WARNING

Be very careful about PageFreeing blocks which are currently MapIntoV86ed to
some VM context. Doing this can result in a crash.
────────────────────────────────────────────────────────────────────────────


PageGetAllocInfo
────────────────────────────────────────────────────────────────────────────

  unsigned long PageGetAllocInfo(flags)
   unsigned flags;

This call is used to obtain information prior to a PageAllocate or
PageReallocate call. It returns the largest block of linear address space
that could be allocated, together with information relating to allocation of
Locked or Fixed memory. There are currently no bits defined in the flags,
this parameter must be set to 0.


Return Value

The return value is a 64 bit long which is actually two 32 bit DWORDs. The
Low DWORD (EAX) is the 32 bit count of free 4K pages in the system which
could be allocated with the PageAllocate as not PageLocked or PageFixed
memory. The High DWORD (EDX) is the 32 bit count of pages available for
allocation as PageLocked pages at the current time.


Comments

You should be careful about making assumptions about being able to turn
around and issue a call to allocate all of the pages returned by this call.
Besides any alignment considerations, it is possible someone could get in
and allocate some or all of the pages before you. This call is intended to
be advisory in nature.

EAX contains the size of the largest available region of linear address
space. EDX contains the count of pages currently available for allocation as
PageLocked pages. Notice that many assumptions are not valid. EAX >= EDX is
not a valid assumption for instance.

You should be very careful about turning around and doing a PageAllocate
with the EAX return from this call. You can cause all sorts of odd behavior
if you take up all of the linear address space. You should allocate memory
on an as needed basis instead of allocating huge blocks of memory most of
which you do not use.


PageGetSizeAddr
────────────────────────────────────────────────────────────────────────────

  unsigned long PageGetSizeAddr(hMem,flags)
     unsigned hMem;
     unsigned flags;

This call is used to get the size and linear address of an existing block of
pages. hMem is the value returned from a previous call to PageAllocate or
PageReAllocate and indicates the block to get the size and address of. There
are currently no bits defined in the flags, this parameter must be set to 0.



Return Value

The return value is a 64 bit long which is actually two 32 bit DWORDS. The
Low DWORD (EAX) is the size in 4K pages of the block. The High DWORD (EDX)
is the 32 bit RING 0 address (offset relative to standard enhanced Windows
Ring 0 DS) of the block. Value (both DWORDs) is 0 if the call failed
(invalid hMem).


Comments

Note that the size of a handle is the total size of the handle and has
nothing to do with what pieces of the handle may or may not be present.


PageLock
────────────────────────────────────────────────────────────────────────────

  unsigned PageLock(hMem,nPages,PageOff,flags)
     unsigned hMem;
     unsigned nPages;
     unsigned PageOff;
     unsigned flags;

This call is used to lock (make present) all or part of an existing memory
handle. hMem is the value returned from a previous call to PageAllocate or
PageReAllocate and indicates the block to be locked. nPages specifies the
count of pages to be locked. PageOff specifies the page offset from the
start of the block of the first page to be locked. nPages together with
PageOff allow all or only part of the hMem block to be locked. An error will
occur if PageOff+nPages is greater than the size of hMem. There are
currently no bits defined in the flags, this parameter must be set to 0.

Current flags bits:

  PageLockedIfDP EQU 00000000000000000000000100000000B

All unused bits must be zero. PageLockedIfDP, if set, indicates that the
lock only needs to be done if the PageSwap device is not direct to hardware.
In the case where the PageSwap device is of type two (direct to hardware),
calls to this routine with PageLockedIfDP set are effectively NOPs. See the
PageAllocate documentation for a description of the different PageSwap
device types and their relevance.


Return Value

Returns nonzero value if the block was succesfully locked, zero if the lock
was unsuccesful (invalid hMem, insufficient memory).


Comments

This call may be issued on hMem blocks which are PageFixed, but this is a
wasted call since PageFixed blocks are always locked (present).

Because of the overcommit associated with demand paging, callers must be
prepared for this call to fail due to unavailability of sufficient memory to
make the region present.

Note that PageLockedIfDP cannot be set on calls made before the init
complete system control call is made. This is because it is not possible to
ask the PageSwap device what type it is before it has been initialized.

Each Page of a handle has an individual lock count. Each lock increments the
counter. The counter must go to 0 for the page to be unlocked. This means
that if the handle is locked 5 times, it has to be unlocked 5 times.

Do not leave handles locked when they don't need to be, unlock handles as
soon as possible to make the physical memory associated available for use by
demand paging.

The Flag bit equates are defined by including VMM.INC, please use the
equates.


PageOutDirtyPages
────────────────────────────────────────────────────────────────────────────

  unsigned PageOutDirtyPages(nPages,flags)
     unsigned nPages;
     unsigned flags;

This call is for use by the demand paging device. It allows the paging
device to periodically "flush" out dirty pages to prevent a large number of
dirty pages from accumulating in the system. nPages is the maximum number of
dirty pages to flush at this time.

Current flags bits:

  PagePDPSetBase  EQU 00000000000000000100000000000000B
  PagePDPClearBase  EQU 00000000000000001000000000000000B
  PagePDPQueryDirty  EQU 00000000000000100000000000000000B

All unused bits must be zero. The PageSwap device may wish to flush out all
dirty pages in the system as part of a "background" activity ("write out
ahead"). These two bits allow this to be done, it allows the caller to
manipulate a variable associated with the page out scan which will cause the
scan to stop. This "base" page number that is set allows the PageSwap device
to tell when the PageOutDirtyPages call has completed a scan of the entire
address space looking for dirty pages. PagePDPSetBase tells
PageOutDirtyPages to set the base page number to the current scan start
point. PagePDPClearBase tells PageOutDirtyPages to clear the base page
number, setting it to NONE. A return value of 0 is used to detect when a
PageOutDirtyPages call has stoped because it has hit the base page. This is
not totally reliable, but is a reasonable approximation, since
PageOutDirtyPages can return 0 because there are no dirty pages (this is
rather unlikely). PagePDPQueryDirty, if set, indicates that the call is to
return the current count of DIRTY demand pageable pages, the nPages argument
and all other flags are ignored if this bit is set (call returns the count
of dirty pages as its sole function).


Return Value

Returns the actual count of dirty pages flushed by the call (0 is valid).

────────────────────────────────────────────────────────────────────────────
WARNING

This call is intended for use by the PageSwap device, others should not be
calling it. Others making this call can disturb the operation of the
PageSwap device.
────────────────────────────────────────────────────────────────────────────


Notes

This call functions something like a partial "commit" of the dirty pages in
the system. Note that ALL of the dirty demand pages can be flushed by
specifying a large value for nPages (like 0FFFFFFFFh).

This call operates only on current page out candidates.

The Flag bit equates are defined by including VMM.INC, please use the
equates.


PageReAllocate
────────────────────────────────────────────────────────────────────────────

  unsigned PageReAllocate(hMem,nPages,flags)
     unsigned hMem;
     unsigned nPages;
     unsigned flags;

This call is used to grow or shrink or reinitialize an existing block of
memory. hMem is the value returned from a previous PageAllocate or
PageReAllocate call and indicates the block to be reallocated. Note that
handles allocated with PageUseAlign set cannot be PageReAllocated. nPages is
a 32 bit unsigned integer which is the new size in 4K pages of the block.

Current flags bits:

  PageZeroInit  EQU 00000000000000000000000000000001B
  PageZeroReInit  EQU 00000000000000000000000000100000B
  PageNoCopy  EQU 00000000000000000000000001000000B
  PageLocked  EQU 00000000000000000000000010000000B
  PageLockedIfDP  EQU 00000000000000000000000100000000B

All unused bits must be zero. PageLocked and PageLockedIfDP, if set,
indicates that if this PageReAllocation is growing the size of the handle,
the pages added to the handle are to be PageLocked or PageLockedIfDP (see
PageAllocate for explanation). If the PageReAllocation is not growing the
handle these bits are ignored. Note that PageFixed is not specified,
PageReAllocation of a PageFixed handle is implied as PageFixed by the handle
itself. PageZeroInit, if set, indicates that if the reallocation is
succesful, and the re-allocation is growing the size of the block, the "grow
area" of the block is to be initialized with value 0 in all bytes. This bit
is ignored on a re-allocation which is not growing the size of the block.
PageZeroReInit , if set, indicates that the entire block is to be
reinitialized with value zero in all bytes of the block. PageNoCopy, if set,
indicates that the previous contents of the block are irrelevant, and don't
need to be copied into the newly sized block. There is no reason that more
than one of these three bits should be set. If none of the bits are set, the
previous contents of the block are copied into the new block, up to the
lesser of the size of the new block, and the size of the old block, and the
"grow area", if any, is not initialized with anything.


Return Value

The return value is a 64 bit long which is actually two 32 bit DWORDS. The
Low DWORD (EAX) is the memory handle of the new block. The High DWORD (EDX)
is the 32 bit RING 0 address (offset relative to standard enhanced Windows
Ring 0 DS) of the block. Value (both DWORDs) is 0 if the reallocation failed
(insufficient memory, handle wrong type, invalid handle).


Comments

Do not make assumptions about the relationship between the passed in hMem
and the Address returned, if specified. Assume that the returned hMem and
address are always different than the passed in hMem and previous address.

In the case where this call fails, the passed in hMem and previous address
of the block remain valid. In the case where this call works and returns a
new hMem and address, the passed in hMem and previous address are no longer
valid (old block has been PageFreed).

────────────────────────────────────────────────────────────────────────────
WARNING

Be very careful about PageReAllocating blocks which are currently
MapIntoV86ed to some VM context. Doing this can result in a crash.
────────────────────────────────────────────────────────────────────────────

PageLocked and PageLockedIfDP should not both be set. Only one, or the
other, or neither are valid settings. Note also that PageLockedIfDP cannot
be set on calls made before the init complete system control call is made.
This is because it is not possible to ask the PageSwap device what type it
is before it has been initialized.

Note that this call can be used to reset the contents of an existing block
to 0 by setting nPages to the current size of the block and setting
PageZeroReInit.

You cannot PageReAllocate a block to size 0, use PageFree.

The Flag bit equates are defined by including VMM.INC, please use the
equates.


PageUnLock
────────────────────────────────────────────────────────────────────────────

  unsigned PageUnLock(hMem,nPages,PageOff,flags)
     unsigned hMem;
     unsigned nPages;
     unsigned PageOff;
     unsigned flags;

This call is used to unlock all or part of an existing memory handle that
was previously locked. hMem is the value returned from a previous call to
PageAllocate or PageReAllocate and indicates the block to be unlocked.
nPages specifies the count of pages to be unlocked. PageOff specifies the
page offset from the start of the block of the first page to be unlocked.
nPages together with PageOff allow all or only part of the hMem block to be
unlocked. An error will occur if PageOff+nPages is greater than the size of
hMem.

Current flags bits:

  PageLockedIfDP  EQU 00000000000000000000000100000000B
  PageMarkPageOut  EQU 00000000000000000010000000000000B

All unused bits must be zero. PageLockedIfDP, if set, indicates that the
unlock only needs to be done if the PageSwap device is not direct to
hardware. In the case where the PageSwap device is of type two (direct to
hardware), calls to this routine with PageLockedIfDP set are effectively
NOPs. See the PageAllocate documentation for a description of the different
PageSwap device types and their relevance. PageMarkPageOut, if set,
indicates that if this unlock actually does unlock the pages (lock count
goes to 0) the pages are to be made prime candidates for page out. This flag
should only be set if it is unlikely that these pages are going to be
touched for a while. Effectively what this does is clear the P_ACC bits of
the pages which causes them to be first level page out candidates.


Return Value

Returns nonzero value if the block was succesfully unlocked, zero if the
lock was unsuccesful (invalid hMem, no part of range is locked).


Comments

This call may be issued on hMem blocks which are PageFixed, but this is a
wasted call since PageFixed blocks cannot be unlocked.

Note that PageLockedIfDP cannot be set on calls made before the init
complete system control call is made. This is because it is not possible to
ask the PageSwap device what type it is before it has been initialized.

Each page of a handle has an individual lock count. Each lock increments the
counter. The counter must go to 0 for the page to be unlocked. This means
that if the handle is locked 5 times, it has to be unlocked 5 times.

The Flag bit equates are defined by including VMM.INC, please use the
equates.


PhysIntoV86
────────────────────────────────────────────────────────────────────────────

  unsigned PhysIntoV86(PhysPage,VMHandle,VMLinPgNum,nPages,flags)
     unsigned PhysPage;
     unsigned VMHandle;
     unsigned VMLinPgNum;
     unsigned nPages;
     unsigned flags;

This call is very similar to the MapIntoV86 call only instead of taking a
memory handle argument, it takes a Physical address (page number). The
intent of this call is to "hook up" a particular VM to the actual Physical
device memory of a device (such as the video memory of a display adaptor).
PhysPage is the physical page number of the start of the region to be
mapped, and indicates the block of physical memory to be mapped. For
instance, to hook up to the 64K of video memory at A000:000, PhysPage would
be A0h and nPages would be 10h. The VMHandle parameter must be a valid VM
handle and indicates the VM into which the map is to occur. VMLinPgNum is
the address in the 1Meg V86 address space of the VM where the map will start
(this is a page number, thus linear address A0000h = page A0h). Alignment
considerations of this address (beyond 4K alignment) are the responsibility
of the caller. Map addresses below page 10h, or above 10Fh will cause an
error. nPages is the number of pages to map. The physical region is assumed
to be contiguous (thus if mapping three pages, they will be PhysPage,
PhysPage+1 and PhysPage+2 in that order). If the physical region is not
contiguous, you will have to issue multiple calls in succession. There are
currently no bits defined in the flags, this parameter must be set to 0.


Return Value

Returns a nonzero value if the map is succesful, returns 0 value if the map
was unsuccesful (invalid VMHandle, map range illegal).


Comments

You are warned to be careful with this call. Very strange things will happen
if you specify a physical region which is unoccupied, or belongs to some
other device.

The page attributes for these pages will be P_USER+P_PRES+P_WRITE. P_DIRTY
and P_ACC will be cleared by the call. PG_TYPE will be set to PG_SYS.

The intent of PhysIntoV86 support for pages between page 10h and
FirstV86Page is to support enhanced Windows devices which have
Allocate_Global_V86_Data_Area a GVDAPageAlign region. Use of mapping in this
region to other addresses can easily crash the system and should be avoided.


Regions which span across FirstV86Page are not allowed.

The reason for the page 10h limitation is that on most versions of the Intel
80386 CPU there is an errata which prevents you from setting up a Linear
Physical address mapping in the first 64k of the address space.


TestGlobalV86Mem
────────────────────────────────────────────────────────────────────────────

  unsigned TestGlobalV86Mem(VMLinAddr,nBytes,flags)
     unsigned VMLinAddr;
     unsigned nBytes;
     unsigned flags;

Some enhanced Windows devices wish to test whether a given piece of V86
address space is LOCAL to a particular VM, or GLOBAL. The reason for this
test is that GLOBAL V86 address ranges are valid and identical in ALL VM
contexts, while LOCAL V86 address ranges are valid in only one VM context.
This difference can yield optimizations. For instance, operations involving
GLOBAL address ranges will typically not need to be "virtualized" in any way
since the range is valid and addressable in ALL VM contexts. LOCAL address
range operations may have to be "virtualized" though since it is possible
for a piece of Virtual Mode code to try and use the address in the "wrong"
VM context where the address range is invalid, or points to the wrong
memory. This call can be used to test whether a V86 address range is GLOBAL
or LOCAL. VMLinAddr is the linear address of the first byte of the V86
address range. This address is relative to the standard RING 0 DS (ie. the
linear address of 02C1:0FC5 would be 02C10 + 0FC5 = 3BD5). nBytes is the
length of the V86 address range in bytes. There are currently no bits
defined in the flags, this parameter must be set to 0.


Return Value

Returns 0 if the address is not a valid V86 address range, or the address
range is LOCAL. Returns 1 if the address range is GLOBAL. Returns 2 if the
address range is partly LOCAL and partly GLOBAL (range overlaps a
GLOBAL/LOCAL boundary). Returns 3 if the address range is GLOBAL but
overlaps with an Instance data region.


Comments

The distinction between GLOBAL and INSTANCE is rather subtle because
INSTANCE pages are "physically global" even though their content is LOCAL.
The physical address of instance data pages never changes, thus instance
pages are GLOBAL in the physical address sense. The content of instance data
regions is per VM though which means they are LOCAL in the sense of "what is
in them".

The MMGR does not know any of the specifics about what is going on in the
regions above FirstV86Page. This routine will return LOCAL for all regions
above FirstV86Page, INCLUDING the A0-FF adapter/ROM BIOS area. Some pieces
of this region may actually be GLOBAL in terms of how they are used, but
this service doesn't know any of the details so it cannot determine this.


19.6  Looking At Physical Device Memory From a VxD
────────────────────────────────────────────────────────────────────────────

VxDs, such as virtual display drivers, that have a certain region of
physical address space associated with them, such as Video Memory, need a
way to look at the device-specific memory when the device is running. The
method by which this is done is by using a service that returns the correct
linear address (relative to the standard Ring 0 DS).

It may seem to be possible for a VxD together with a Protected Mode
application to implement its own private "virtual memory" system by hooking
into the IDT and handling page faults itself. You are strongly warned not to
try and undertake an implementation of this sort at this time. It is
realized that support of such an architecture is a desirable feature for
implementation of such things as "virtual files" and "virtual memory mapped
devices". This feature will be addressed in a future version of Windows by
adding new VMM services, and/or modifying existing services to support it.
Since support for this is planned, any and all work that you might undertake
on the current version of Windows to implement this will be rendered
obsolete when support for it is added to the VMM.


MapPhysToLinear
────────────────────────────────────────────────────────────────────────────

  unsigned MapPhysToLinear(PhysAddr,nBytes,flags)
     unsigned PhysAddr;
     unsigned nBytes;
     unsigned flags;

PhysAddr is the physical address of the start of the region to be looked at.
This is simply the 32 bit physical address, there are no alignment
considerations. Physical addresses start at 0, thus the address of physical
page 0A0h is 0A0000h. nBytes is the length of the physical region in bytes
starting from PhysAddr. This parameter is used to verify that the entire
range is addressable. There are currently no bits defined in the flags, this
parameter must be set to 0.


Return Value

Returns the RING 0 DS offset of the first byte of the physical region. Will
return 0FFFFFFFFh if the specified range is not addressable.

────────────────────────────────────────────────────────────────────────────
WARNING

You are warned to be careful with this method. Use of this for purposes
beyond looking at device specific physical memory is extremely dangerous and
is not approved.
────────────────────────────────────────────────────────────────────────────


Comments

Physical addresses do not move. It is perfectly fine to get the linear
address of a physical region at Device_Init device call time and then use it
later. You do not have to keep recalling MapPhysToLinear every time you want
to look at the region.

For instance to look at physical page A0h you would do this:

  VMMCall _MapPhysToLinear,<0A0000h,10000h,0>

DS:[EAX] now addresses this physical page. Physical memory is mapped
contiguously at this selector so Page 0A1h would be 4096 bytes beyond the
above address.


19.7  Data Access Services
────────────────────────────────────────────────────────────────────────────

These services are used to get the contents of public memory manager
variables. All of these services return the value of the associated variable
in EAX.


GetFirstV86Page
────────────────────────────────────────────────────────────────────────────

  unsigned GetFirstV86Page()

This call returns the page number of the first page of VM specific V86
memory.


Comments

FirstV86Page MOVES during device initialization. Do not get the value at
device init time, and then use it later, as the value is invalid.


GetNulPageHandle
────────────────────────────────────────────────────────────────────────────

  unsigned GetNulPageHandle()

This call returns the memory handle of the system NUL page.


GetAppFlatDSAlias()
────────────────────────────────────────────────────────────────────────────

  unsigned GetAppFlatDSAlias()

This call returns a selector which can be used by protected mode
applications to look at the same data that the standard RING 0 DS looks at.
This is useful when a VxD wishes to provide a protected mode service to
applications and wants the application to be able to address the same memory
that the VxD does.


Comments

This selector is read only. This is so that the Windows address space is
protected from a misbehaved application. It is not recommended that you
build a read/write version of this selector. If the application needs to
WRITE you should build a descriptor with a much more restricted Base and
Limit so that the application can only modify those things which it is
allowed to modify.

This selector is RPL = DPL = Protected Mode Application Privilege. Notice
that a VxD can also use this selector if desired even though the devices run
at a different privilege level. Its type is "USE 16", this doesn't mean much
since it is a data selector.

This is a GDT selector.

────────────────────────────────────────────────────────────────────────────
WARNING

You must not do a Free_GDT_Selector on this selector. It is not protected,
and so it will get freed. Then anyone using it will fault and crash the
system. This selector is provided to prevent multiple devices from creating
multiple versions of the same selector and wasting GDT entries
unnecessarily.
────────────────────────────────────────────────────────────────────────────

Notice that enhanced Windows is "USE 32", therefore a protected application,
which is "USE 16", will have to use the DB 67h addressing mode override on
its instructions to get 32 bit addressing (MASM will do this for you
automatically if you set things up correctly).

This service can be used to discover what protection ring protectedmode
applications run at by doing a LAR on the returned selector. Be very careful
about what you do with this bit of information.


19.8  Special Services For Protected Mode APIs
────────────────────────────────────────────────────────────────────────────

These services are provided to support VxDs that need to manipulate
protected-mode address space. For example, applications running in protected
mode need a way to map regions of protected mode, segmented address space
into the virtual machine's virtual 8086 context. A specific example is the
MS-DOS INT 21 API. The data pointed to on the INT 21 calls needs to be
mapped into the VM's V86 address space so that MS-DOS can access it and
perform the requested operation.

────────────────────────────────────────────────────────────────────────────
WARNING

Do not use these services for purposes other than their intended use. These
calls can be quite dangerous and can result in strange behavior or crashes
if misused.
────────────────────────────────────────────────────────────────────────────


GetV86PageableArray
────────────────────────────────────────────────────────────────────────────

  unsigned GetV86PageableArray(VMHandle,ArrayBufPTR,flags)
  unsigned VMHandle;
  unsigned ArrayBufPTR;
  unsigned flags;

This call is used to obtain a copy of the bit array of pages whose behavior
has been modified via SetResetV86Pageable. This allows the caller to
determine which regions of the VM V86 address space have had the normal
lock/unlock behavior modified. VMHandle specifies the VM to get the bit map
of. ArrayBufPTR points to a buffer large enough to contain the array. The
assignment array is an array of 100h bits, one bit for each page in the
range 0-100h. Thus the size of the array is ((100h/8)+3)/4 = 8 DWORDS. Bits
in the array which are set (=1) indicate pages whose normal lock/unlock
behavior is disabled, bits which are clear (=0) indicate pages whose
behavior is normal. Thus to test the bit for page number N (0

  mov ebx, N MOD 32    ; Bit number in DWORD
  mov eax, N / 32    ; DWORD index into array
  bt dword ptr ArrayBufPTR[eax*4],ebx  ; Test bit for page N
  jnc short PageNormal
      PageModified:

Note that this code is mearly intended to illustrate how the bit array
works. This code is not the most efficient, or the only way to implement
this test. There are currently no bits defined in the flags, this parameter
must be set to 0.


Return Value

Returns non-zero if succesfull, returns zero if the bit array could not be
returned (Invalid VMHandle).


Comments

Making this call on a VM whose VMStat_PageableV86 bit is clear is not an
error, it simply returns a bit array whose bits are all 0.


LinMapIntoV86
────────────────────────────────────────────────────────────────────────────

  unsigned long LinMapIntoV86(HLinPgNum,VMHandle,VMLinPgNum,nPages,flags)
     unsigned HLinPgNum;
     unsigned VMHandle;
     unsigned VMLinPgNum;
     unsigned nPages;
     unsigned flags;

This call is provided to assist the interface address mapper functions. Its
purpose is to provide a way for the address mapper to map regions of
protected mode address space into a VM V86 address space so that API calls
can be performed. This calls operation is very similar to MapIntoV86, the
difference being that instead of taking a memory handle, it takes a linear
address. The call duplicates the memory map down into the indicated VM's V86
address range. HLinPgNum, together with nPages, indicates the region of
protected mode address space, or V86 address space that is to be mapped.
This is a page number, linear address 60610000h would be passed in as
60610h. As with MapIntoV86 there are implied PageLock and PageUnlocks. Note
that the linear address is relative to the standard enhanced Windows Ring 0
DS selector. The VMHandle parameter must be a valid VM handle and indicates
the V86 space into which the map is to occur. VMLinPgNum is the address in
the 1Meg VM V86 address space where the map will start (this is a page
number, thus linear address 60000h = page 60h). Alignment considerations of
this address (beyond 4K alignment) are the responsibility of the caller. Map
addresses below page 10h, or above 10Fh will cause an error. nPages is the
number of pages to map. Note that if HLinPgNum is a V86 page number (at the
LOW V86 address (at the LOW V86 address <= page 100h) the call does nothing
except return the HLinPgNum parameter in EDX. There are currently no bits
defined in the flags. This parameter must be 0.


Return Value

The return value is a 64 bit long which is actually two 32 bit DWORDS. The
Low DWORD (EAX) is a nonzero value if the map is succesful, returns 0 in eax
if the map was unsuccesful (invalid address range, invalid VMHandle, map
range illegal, size discrepancy, insufficient memory for implied PageLock).
The High DWORD (EDX) is only valid if EAX is nonzero. It is set to the
VMLinPgNum parameter if the HLinPgNum parameter was not a LOW V86 space
address, otherwise it is set to the HLinPgNum parameter. In short, EDX is
the V86 address where the memory is mapped.


Comments

As with MapIntoV86 there is an implied PageLock which is performed on all of
the pages mapped. This is consistent with the fact that V86 memory cannot be
Demand Paged while the VM is in a runable state. Whenever the V86 memory
mapping is changed via LinMapIntoV86, the previous memory that was mapped in
the VM is unlocked. The correct way to think of this is that there is an
implied PageLock whenever memory is mapped into a V86 context, and an
implied PageUnlock whenever it is "unmapped" from the V86 context. This
"unmapping" can occur when: A different handle (including the NulPageHandle)
is MapIntoV86ed to the region, or a PhysIntoV86 is performed to the region.


The V86 region mappped into by this call should be MapIntoV86ed with the
NulPageHandle when the V86 mapping region is no longer needed. There is
nothing to prevent you from mapping the same protected mode linear address
into multiple places in a VM, or into multiple VMs. Such operations are not
particularly advisable though. For one thing, the reporting of memory owned
by a VM will be disturbed.

The reason this call exists is because a protected mode API mapper does not
have access to the memory handles associated with the various regions of
protected mode address space. VxDs which do have access to the memory
handles of the memory to be mapped should be using MapIntoV86 to map the
memory, not this routine.

For regions in the Physical addressing region this call will convert into a
PhysIntoV86 call.

For regions in the HIGH VM Linear addressing region this call will perform a
map of the memory from one VM into another VM (or into a different location
in the same VM). NOTE CAREFULLY: The intent of this support is to provide a
way for the V86MMGR device to map a region of V86 address space which is
currently LOCAL to one VM into a GLOBAL region that is addressable by all
VMs. This type of API is needed by network API mappers. Do not use this
capability in your VxD, use the V86MMGR service. The details of this aspect
of operation will change in a later release and code using the old method
will not function properly.

The page attributes for these pages will be P_USER+P_PRES+P_WRITE. P_DIRTY
and P_ACC will be cleared by the call. PG_TYPE will be set to whatever the
type of the pages are at its protected mode linear address.

The intent of LinMapIntoV86 support for pages between page 10h and
FirstV86Page is to support enhanced Windows devices which have
Allocate_Global_V86_Data_Area a GVDAPageAlign region. Use of mapping in this
region to other addresses can easily crash the system and should be avoided.


Regions which span across FirstV86Page are not allowed.

The reason for the page 10h limitation is that on most versions of the Intel
80386 CPU there is an errata which prevents you from setting up a Linear !=
Physical address mapping in the first 64k of the address space.


LinPageLock
────────────────────────────────────────────────────────────────────────────

  unsigned LinPageLock(HLinPgNum,nPages,flags)
     unsigned HLinPgNum;
     unsigned nPages;
     unsigned flags;

This call is provided to assist the interface address mapper functions. Its
purpose is to provide a way for the address mapper to lock regions of
protected mode address space so that API calls can be performed. This calls
operation is very similar to PageLock, the difference being that instead of
taking a memory handle, it takes a linear address. HLinPgNum, together with
nPages, indicates the region of protected mode address space that is to be
locked. This is a page number, linear address 60610000h would be passed in
as 60610h. Note that the linear address is relative to the standard enhanced
Windows Ring 0 DS selector.

Current flags bits:

  PageLockedIfDP  EQU 00000000000000000000000100000000B

All unused bits must be zero. PageLockedIfDP, if set, indicates that the
lock only needs to be done if the PageSwap device is not direct to hardware.
In the case where the PageSwap device is of type two (direct to hardware),
calls to this routine with PageLockedIfDP set are effectively NOPs. See the
PageAllocate documentation for a description of the different PageSwap
device types and their relevance.


Return Value

Returns a nonzero value if the lock is succesful, returns 0 value if the
lock was unsuccesful (invalid address range, insufficient memory for lock).



Comments

SEE PageLock.


LinPageUnLock
────────────────────────────────────────────────────────────────────────────

  unsigned LinPageUnLock(HLinPgNum,nPages,flags)
     unsigned HLinPgNum;
     unsigned nPages;
     unsigned flags;

This call is provided to assist the interface address mapper functions. Its
purpose is to provide a way for the address mapper to unlock regions of
protected mode address space after API calls are performed. This calls
operation is very similar to PageUnLock, the difference being that instead
of taking a memory handle, it takes a linear address. HLinPgNum, together
with nPages, indicates the region of protected mode address space that is to
be unlocked. This is a page number, linear address 60610000h would be passed
in as 60610h. Note that the linear address is relative to the standard
enhanced Windows Ring 0 DS selector.

Current flags bits:

  PageLockedIfDP  EQU 00000000000000000000000100000000B
  PageMarkPageOut  EQU 00000000000000000010000000000000B

All unused bits must be zero. PageLockedIfDP, if set, indicates that the
unlock only needs to be done if the PageSwap device is not direct to
hardware. In the case where the PageSwap device is of type two (direct to
hardware), calls to this routine with PageLockedIfDP set are effectively
NOPs. See the PageAllocate documentation for a description of the different
PageSwap device types and their relevance. PageMarkPageOut, if set,
indicates that if this unlock actually does unlock the pages (lock count
goes to 0) the pages are to be made prime candidates for page out. This flag
should only be set if it is unlikely that these pages are going to be
touched for a while. Effectively what this does is clear the P_ACC bits of
the pages which causes them to be first level page out candidates.


Return Value

Returns a nonzero value if the unlock is succesful, returns 0 value if the
unlock was unsuccesful (invalid address range).


Comments

SEE PageUnLock.


PageCheckLinRange
────────────────────────────────────────────────────────────────────────────

  unsigned PageCheckLinRange(HLinPgNum,nPages,flags)
     unsigned HLinPgNum;
     unsigned nPages;
     unsigned flags;

This call is provided to assist the interface address mapper functions. Its
purpose is to provide a way for the address mapper to validate an intended
range for LinPageLock or LinMapIntoV86. Sometimes a MAXIMUM length range is
specified because the true range is unknown. This call will return an
adjusted nPages argument which will be adjusted down in size if the
specified range crosses an unreasonable boundary. HLinPgNum, together with
nPages, indicates the region of protected mode address space that is to be
checked. This is a page number, linear address 60610000h would be passed in
as 60610h. Note that the linear address is relative to the standard enhanced
Windows Ring 0 DS selector. There are currently no bits defined in the
flags, this parameter must be 0.


Return Value

Returns an adjusted nPages agrument. This will be zero if the range is
totally unreasonable, and will return nPages if no adjustment was needed.


Comments

The end of a handle is a boundary that will result in an adjustment.


PageDiscardPages
────────────────────────────────────────────────────────────────────────────

  unsigned PageDiscardPages(LinPgNum,VMHandle,nPages,flags)
  unsigned LinPgNum;
  unsigned VMHandle;
  unsigned nPages;
  unsigned flags;

This call is provided to assist management of PM applications by providing a
way to mark pages as "no longer in use". What this does is allow regions
which were previously "in use" to be "discarded". This means that the page
does not have to be "paged in" to make it present, thus eliminating the disk
access required for the page in. LinPgNum and nPages together specify the
range to be discarded. LinPgNum is a page NUMBER. If LinPgNum is < 110h, or
at a VM high linear address, then the range lies in a VM and the VMHandle
parameter specifies the VM. In this case, all pages of the range must be
marked V86Pageable or the call will fail. Pages in the range which are not
present or are locked are ignored, this call effects only demand pageable
pages.

Current flags bits:

  PageZeroInit  EQU 00000000000000000000000000000001B
  PageDiscard  EQU 00000000000000010000000000000000B

Setting PageDiscard indicates that a full discard is to take place, the
P_ACC and P_DIRTY bits in the page table entrys for the pages are both
cleared. If PageDiscard is clear, all the call does is clear the P_ACC bit
in the page table entrys for the pages making them primary page out
candidates (the DIRTYness and content of the pages is preserved in this
case). Setting PageZeroInit is relevant only if PageDiscard is also set, and
it indicates that the pages are to be marked "zero the contents of this page
the next time it is paged in". In this case this subsequent page in is a NOP
since the pages have been discarded, this simply causes the pages to come
back in with a known value (0) in them instead of random garbage.


Return Value

Returns a non-zero value if successful, otherwise it returns zero (invalid
range or VM handle).


Comments

The Flag bit equates are defined by including VMM.INC, please use the
equates.


SelectorMapFlat
────────────────────────────────────────────────────────────────────────────

  unsigned SelectorMapFlat(VMHandle,Selector,flags)
     unsigned VMHandle;
     unsigned Selector;
     unsigned flags;

This call is provided to assist the interface address mapper functions. Its
purpose is to provide a way for the address mapper to get the RING 0 DS
offset of the base of a particular GDT or LDT selector. This call assists
the address mapper in converting a Selector:Offset16 or Selector:Offset32
pointer into its "flat model" linear address which can then be passed to
LinMapIntoVM. Selector is a GDT or LDT selector (note that the argument is a
DWORD not a WORD) value to get the base address of. The VMHandle parameter
is ignored if Selector is a GDT selector. If Selector is an LDT selector,
then VMHandle indicates the appropriate VM context for the Selector. There
are currently no bits defined in the flags, this parameter must be 0.


Return Value

Returns the linear address of the base of the selector if succesful, returns
FFFFFFFFh if it is unsuccesful (invalid selector).


Comments

You can pass this routine the standard enhanced Windows RING 0 DS selector,
and it will return 0 as the base. This is a silly thing to do, but it does
work.

The VMHandle parameter must be valid for LDT selectors.


SetResetV86Pageable
────────────────────────────────────────────────────────────────────────────

  unsigned SetResetV86Pageable(VMHandle,VMLinPgNum,nPages,flags)
  unsigned VMHandle;
  unsigned VMLinPgNum;
  unsigned nPages;
  unsigned flags;

This call allows the normal locking/unlocking behavior associated with a
specific range of V86 memory to be modified. VMHandle is the VM in which the
behavior is being modified. VMLinPgNum is the address in the 1Meg V86
address space where the behavior modification will start (this is a page
number, thus linear address 60000h = page 60h). Alignment considerations of
this address (beyond 4K alignment) are the responsibility of the caller. Map
addresses below FirstV86Page, or above 100h will cause an error. nPages is
the number of pages to modify the behavior of. Normally a MapIntoV86 causes
the memory that is mapped to be locked. In the case where this particular VM
is currently running a Protected Mode application, it is desirable to undo
the lock, and change this normal lock/unlock behavior. This allows those
unused pieces of the V86 address space to be paged out and the memory they
are using to be used by someone else. Note that we can only undo this normal
behavior because the behavior of the protected mode application is well
known. In particular, we know that none of the V86 memory that is being
unlocked contains code that is executed, or data that is touched, at
interrupt time (including software interrupt time). The typical use of this
call is by the enhanced Windows device which loads a protected mode
application. When the PM app is loaded, the device calls SetRestV86Pageable
with the PageSetV86Pageable bit set on those pieces of the V86 address space
above FirstV86Page which can be unlocked; this is typically all of the V86
memory above FirstV86Page which is currently MS-DOS Free. NOTE that MS-DOS
data areas such as the 100h byte Program Header Prefix must not be included
in the ranges because they are accessed by MS-DOS. Similarly, when the
Protected Mode application Exits, the application loader calls
SetResetV86Pageable with PageClearV86Pageable set, on the V86 memory it had
initially modified during the load.

The other aspect of the behavior that can be modified has to do with the
"other memory" (the memory that is not V86Pageable) in the VM. Normally this
memory is locked, except when the pager is type 2 (direct to hardware). Not
locking the V86 memory allows VM's V86 pages to also be Demand Paged. This
has the benefit of allowing MS-DOS applications to also run in a Demand
Paged environment. Sometimes though, this is an undesired behavior because
of the paging latency which it introduces in the VM. The V86IntsLocked bit
of a VM allows this aspect to be controled. Setting the V86IntsLocked
behavior causes the "other memory" to always be locked, even if the pager is
type 2. Setting this behavior has two important effects:


  ■   There is never any "paging latency" while the virtual mode code in
      this VM is running. This prevents time critical V86 code from having
      its timing severly disturbed due to the paging overhead.

  ■   The paging device can enable interrupts in this VM when it is
      performing paging operations because it knows that a nested page fault
      will not occur from this VM since all of its interrupt time code is
      always locked.


Current flags bits:

  PageSetV86Pageable  EQU 00000000000000000000001000000000B
  PageClearV86Pageable  EQU 00000000000000000000010000000000B
  PageSetV86IntsLocked  EQU 00000000000000000000100000000000B
  PageClearV86IntsLocked  EQU 00000000000000000001000000000000B

All unused bits must be zero. PageSetV86Pageable, if set, indicates that the
normal locking behavior of MapIntoV86 is to be disabled (V86 memory can be
paged) for the indicated region. PageClearV86Pageable, if set, indicates
that the normal locking behavior is to be enabled on the indicated region.
PageSetV86IntsLocked, if set, indicates that the "lock all V86 memory that
is not V86Pageable regardless of pager type" behavior is to be enabled.
PageClearV86IntsLocked, if set, indicates that the "lock all V86 memory that
is not V86Pageable regardless of pager type" behavior is to be disabled.
Note that only one of these bits can be set on a call. Setting more than one
bit will result in an error. There are two bits in CB_VM_Status that
indicate the current state of these behaviors:

  VMStat_PageableV86  EQU 00000000000000000000100000000000B
  VMStat_V86IntsLocked  EQU 00000000000000000001000000000000B

The VMStat_PageableV86 bit is set if any regions behavior has been modified
(there is at least one non zero bit in the array returned by
GetV86PageableArray). The VMStat_V86IntsLocked bit is set if the "lock
regardless of pager type" behavior has been enabled in this VM.


Return Value

Returns non-zero value if the set or clear worked, zero if the current state
of the VM was not consistent with the call (invalid VMHandle,
VMStat_PageableV86 or VMStat_V86IntsLocked state inconsistent with setting
of PageSet/ClearV86Pageable or PageSet/ClearV86IntsLocked bit in flags,
range invalid) or the lock of the memory associated with
PageClearV86Pageable or PageSetV86IntsLocked failed.


Comments

The intent of this call is to better support Protected mode applications
running in a VM, not to allow you to randomly make v86 parts of VMs
pageable! Do not issue this call on a VM unless you are loading a Protected
mode app into it.

The V86MMGR device makes a PageSetV86IntsLocked call on VMs which are
created with their base memory specified as locked.

Extreme care must be used when manipulating the PageableV86 behavior of
regions above A000:0. This should not be done unless the region is GLOBAL or
LOCAL Assign_Device_V86_Pages owned by the caller.

There is no REGION associated with PageSetV86IntsLocked and
PageClearV86IntsLocked calls. The IMPLIED region is always "everything that
isn't V86Pageable". For this reason the HLinPgNum and nPages arguments
should be set to 0 on these calls.

VMM.INC contains equates for all of the flag bits described, use the
equates.


19.9  Instance Data Management
────────────────────────────────────────────────────────────────────────────

The purpose of these services is to provide a means of identifying to the
system those areas of virtual 8086 mode memory (V86 memory) that contain per
Virtual Machine or "Instance" data. Each of the VMs in the system has its
own, private instance of this data and anything the VM does to the values in
these locations has no effect on other VMs since the values are different in
each VM.

────────────────────────────────────────────────────────────────────────────
NOTE

All of these calls use the USE32 C calling convention. The true name of the
procedure has an underscore in front (i.e., AddInstanceItem is actually
_AddInstanceItem), and the arguments are pushed right to left (unlike the
PL/M calling convention used by Windows, which is left to right). The return
value(s) is returned in C standard EDX:EAX. It is the responsibility of the
callerto clear the arguments off the stack. Registers EAX, ECX, and EDX are
changed by calls. Registers DS, ES, EBP, EDI, ESI, and
@AE@%EBX are preserved.
────────────────────────────────────────────────────────────────────────────


AddInstanceItem
────────────────────────────────────────────────────────────────────────────

  unsigned AddInstanceItem(InstStrucPTR,flags)
     unsigned InstStrucPTR;
     unsigned flags;

This call is used to identify a region of instance data in the V86 address
space. InstStrucPTR is a pointer to an instance data identification
structure which has this form:

  InstDataStruc struc
     InstLinkF  dd ? ; RESERVED SET TO 0
     InstLinkB  dd ? ; RESERVED SET TO 0
     InstLinAddr  dd ? ; Linear address of start of block
     InstSize  dd ? ; Size of block in bytes
     InstType  dd ? ; Type of the block
  InstDataStruc ends

The InstLinkF and InstLinkB fields are filled in by the Instance data
manager and cannot be used by the caller. InstLinAddr defines the start of
the block of instance data, NOTE THAT THIS IS NOT IN SEG:OFFSET FORM, it is
a linear address. Thus the correct value for 40:2F would be 42F. InstSize is
the size of the instance data block in bytes starting at InstLinAddr.
InstType defines one of two types of instance data:

  INDOS_Field   equ  100h ; Bit indicating INDOS switch requirements
  ALWAYS_Field  equ  200h ; Bit indicating ALWAYS switch requirements

ALWAYS_Field type indicates that the field must always be switched when a VM
is switched. All instance data sepcified by VxDs should be of this type.
INDOS_Field type is reserved for special types of MS-DOS internal data which
only need to be switched with the VM if the VM is currently INDOS.

There are currently no bits defined in the flags, this parameter must be set
to 0.


Return Value

Returns nonzero value if the instance data block was succesfully added to
the instance list, zero if the block was unsuccesful added (This is probably
a FATAL error).

────────────────────────────────────────────────────────────────────────────
NOTE

There are two basic ways to allocate the space for the InstDataStrucs
pointed to with InstStrucPTR. The first is to simply staticly allocate them
in the INIT data segment. The space they occupy will then be reclaimed when
the INIT space is reclaimed. The other way is to allocate them on the System
heap using HeapAllocate. The space can then be freed by HeapFreeing all of
the heap handles in the device Sys_VM_Init code which is called after all of
the system initialization (including the instance data initialization) is
done.

────────────────────────────────────────────────────────────────────────────
WARNING


If you allocate space for InstDataStrucs on the heap you must be sure NOT to
HeapReAllocate the heap blocks after passing the address to AddInstanceItem
because this will invalidate the InstStrucPTR value you previously passed to
AddInstanceItem.
────────────────────────────────────────────────────────────────────────────


────────────────────────────────────────────────────────────────────────────
NOTE

This routine is in the init segment of enhanced Windows. It can therefore
only be called during system initialization. Trying to call it after system
initialization and the system INIT segment space has been reclaimed will
result in a fatal page fault.
────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────

Once this call is made, the caller must not ever touch the InstDataStruc
pointed to again. The caller has passed control of this data block to the
instrance data manager and tampering with it will result in the instance
data manager failing to identify the instance data correctly.

Note that only one, contiguous region of instance data can be identified
with each structure. It is a good idea for the caller to coalesce adjacent
blocks of instance data it is identifying in order to cut down the call
overhead and data space requirements, but this is not required.

There is a declaration of the InstDataStruc data structure in VMM.INC.


MMGR_Toggle_HMA
────────────────────────────────────────────────────────────────────────────

  unsigned MMGR_Toggle_HMA(VMHandle,flags)
   unsigned VMHandle;
   unsigned flags;

This call is an interface to the Instance data manager which allows devices
such as the V86MMGR XMS device to control the behavior of the "highmem"
memory area, or "HMA", of a VM (V86 linear pages 100h through 10Fh). Any
device which wishes to modify the "1Meg Address Wrap" behavior of a VM MUST
use this call to inform the Instance data manager what is going on. This is
because the Instance manager must know whether 1Meg Address Wrap is on or
off to manage the instance data correctly for a VM. VMHandle is a valid
enhanced Windows VM handle which indicates the VM to which the call is to be
applied. Current flags bits:

  MMGRHMAPhysical  EQU 00000000000000000000000000000001B
  MMGRHMAEnable  EQU 00000000000000000000000000000010B
  MMGRHMADisable  EQU 00000000000000000000000000000100B
  MMGRHMAQuerry  EQU 00000000000000000000000000001000B

All unused bits must be zero. One, and only one of MMGRHMAEnable,
MMGRHMADisable, MMGRHMAQuerry BITS must be specified, the call will have
random results if this is not true. MMGRHMAPhysical bit is a modifier which
modifies the operation of the MMGRHMAEnable bit: See discussion of
MMGRHMAEnable. MMGRHMADisable, if set, causes the Instance manager to
restore the normal Wrap mapping for pages 100 through 10F thus Disabling the
HMA. This is a REMAP of pages 00h through 0Fh of the VM and causes the VMs
address space to "wrap" back to address zero for addresses >1Meg as it does
on an 8086 processor. MMGRHMAEnable, if set, disables 1Meg address wrap in
the VM, thus Enabling the HMA. Exactly what this does is controlled by the
MMGRHMAPhysical bit. If MMGRHMAPhysical is set, MMGRHMAEnable causes
PHYSICAL pages 100h through 10Fh to be mapped in Linear pages 100h through
10Fh of the VM consistent with the operation of a Global HMA which is shared
by all VMs. If MMGRHMAPhysical is not set, Linear pages 100h through 10Fh
will be marked as not present System Pages in the VM. It is then up to the
CALLER to map some other memory handle into this region of the VM after this
call. This is consistent with the operation of a per VM HMA. Note that if
the VM accesses these pages before this mapping is set up, an erroneaous
page fault will occur which will crash the VM, or the system. MMGRHMAQuerry,
if set, returns the current state of the HMA in the VM.


Return Value

This call has no return value unless MMGRHMAQuerry was specified in the
flags. In this case the call will return value 0 if the HMA is Disabled
(1Meg address wrap is enabled), and it will return a nonzero value if the
HMA is Enabled (1Meg address wrap is disabled).


Comments

This call is reserved for the V86MMGR XMS device. Other devices should not
be using this call. Modifying the Wrap state of a VM without the V86MMGR XMS
device knowing about it will probably result in a state error and a crash.

The device issuing this call must be a device which has succesfully Globally
or Locally Assign_Device_VM_Paged pages 100h through 10Fh in the indicated
VM. This is not a call which multiple devices should make for a VM as doing
so will cause confusion between the devices.

When VMs are created, they are created with the HMA Disabled (1Meg Address
Wrap enabled) consistent with normal operation on an 8086 processor. The
device responsible for the HMA in a VM must adjust this in its Create_VM
device call if needed.

Note that no distinction is drawn on the MMGRHMAQuerry return between
MMGRHMAPhysical being specified, or not specified on a previous
MMGRHMADisable call.

────────────────────────────────────────────────────────────────────────────
NOTE

Instance data is not allowed in the hma.
────────────────────────────────────────────────────────────────────────────

The flag bit equates are in VMM.INC, please use the equates.


19.10  Looking At V86 Address Space
────────────────────────────────────────────────────────────────────────────

From time to time, VxDs may wish to look at or modify some piece of the
virtual 8086 mode address space of a VM that is not the current VM. The
documented way to do this is as follows.


CB_High_Linear
────────────────────────────────────────────────────────────────────────────

There is a Control Block variable which is a linear address of the start of
the VM's address space. Thus to look at VM linear adress 40:17 with EBX
being the VM Handle of the VM you're interested in you would do this:

  mov esi,(40h SHL 4) + 17h
  add esi,[ebx.CB_High_Linear]

ESI now points to this location in the V86 address space. This can be used
to look at, modify any V86 address including instance data addresses.

────────────────────────────────────────────────────────────────────────────
NOTE

No code should EVER touch a part of V86 address space at its "low" address
(>=0,<=400000h) EVEN FOR THE CURRENT VM. There is NO REASON to do this, use
CB_High_Linear in ALL cases to look at V86 addresses.
────────────────────────────────────────────────────────────────────────────






Chapter 20  I/O Services and Macros
────────────────────────────────────────────────────────────────────────────

This chapter documents the services available for I/O. Also included are two
macros and a discussion explaining their usefulness.

See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.

When a virtual machine executes an instruction that reads or writes data
from an I/O port, the 80386 looks up the port number in the I/O Permission
Map (IOPM). If the corresponding bit in the IOPM is set, then the
instruction will cause a protection fault.

Enhanced Windows provides services that virtual devices use to trap I/O. The
first thing a virtual device must do is hook the port while the device is
being initialized. This is done by calling a service called
Install_IO_Handler. It takes two parameters: the number of the I/O port to
hook and the address of a callback procedure.

The I/O services and macros supported by enhanced Windows are described in
this chapter in the following order:


  ■   Enable_Global_Trapping

  ■   Disable_Global_Trapping

  ■   Enable_Local_Trapping

  ■   Disable_Local_Trapping

  ■   Install_IO_Handler

  ■   Install_Mult_IO_Handlers

  ■   Simulate_IO



20.1  Handling Different I/O Types

The value passed in ECX determines the type of input or output as specified
by Table 20.1.

Table 20.1  I/O Register Values

╓┌─────────────────────┌─────────────────────────────────────────────────────╖
Value                 Type of input/output
────────────────────────────────────────────────────────────────────────────
00H                   Byte input
04H                   Byte output
08H                   WORD input
0CH                   WORD output
10H                   DWORD input
14H                   DWORD output
────────────────────────────────────────────────────────────────────────────


Masks that apply only to string I/O are shown in Table 20.2.

Table 20.2  String I/O Register Values

╓┌────────────┌──────────────────────────────────────────────────────────────╖
Value        Type of input/output
────────────────────────────────────────────────────────────────────────────
Value        Type of input/output
────────────────────────────────────────────────────────────────────────────
20H          String I/O
40H          Repeated string I/O
80H          32-bit addressing mode string I/O
100H         Reverse string I/O (VM's direction flag is set)
────────────────────────────────────────────────────────────────────────────


For all string I/O operations, the high WORD of ECX contains the segment for
the string I/O. This allows VxDs to ignore the issues of segment overrides
on these instructions; VMM has already determined the correct segment value.
Thus, a value of 3247016CH would specify that the VM is doing word reverse
repeated string output from 3247:SI.

For example:

High word = segment 3247 0Ch = Word output 20h = String I/O 40h = Repeated
string I/O 100h = Reverse I/O

It would be unreasonable to expect every VxD to support 48 different types
of I/O. Therefore, the VxD environment only requires VxDs to support byte
input and output, even though a VxD can directly support any type of I/O
that is appropriate. For example, there is no reason for the Virtual Printer
Device (VPD) to support WORD input and output since printer ports are only
8-bits wide.

However, the VxDs for devices with 16-bit ports can directly support WORD
I/O as well as byte I/O.

Furthermore, devices such as disk drives might need to directly emulate
string I/O for some ports to achieve acceptable performance. A device can
emulate some types of I/O and ignore others.

But what happens if someone does WORD string output to a printer port? You
canot just throw the I/O away! For this reason, enhanced Windows has a
catch-all routine called Simulate_IO that converts I/O into something the
virtual device can understand. Notice in the port trap code of the VPD
example that entry points start with the Emulate_Non_Byte_IO macro. This
macro generates the following code:

  cmp ecx, 4
   jbe SHORT Foo
   VMMjmp Simulate_IO

  Foo:

So, if a VM attempted to do non-repeated forward word string I/O, the
following sequence of calls to the VPD trap code would be issued:

Call VPD trap with:

EBX = VM handle EDX = 358h (Port #) ECX = 23A8002Ch (String I/O from segment
23A8h) EBP = Client register structure

VPD jumps to Simulate_I/O which calls VPD again with:

EBX = VM handle EDX = 358h (Port #) ECX = 0Ch (0Ch = Word output) AX = Word
to output EBP = Client register structure

VPD jumps to Simulate_I/O which calls VPD again with:

EBX = VM handle EDX = 358h (Port #) ECX = 04h (04h = Byte output) AL = Byte
to output EBP = Client register structure

VPD then simulates the byte output and returns.

Notice that the high-order byte of the word output would be sent to the trap
routine for VPD trap port # +1. So, if VPD is trapping port 358H, then word
output to this port will be converted into byte output to ports 358H and
359H (exactly the way the hardware works).


20.2  I/O Macros

There are two useful macros for I/O trap routines. The first macro,
Emulate_Non_Byte_IO, generates the following code:

  cmp ecx, Byte_Output
    jbe SHORT Is_Byte_IO
    VMMjmp Simulate_IO
    jnz Is_Byte_Input
  Is_Byte_IO:

Dispatch_Byte_IO, the second useful macro, takes two arguments. The first is
the destination for byte input, and the second is the destination for byte
output. This macro passes back all non-byte I/O to Simulate_IO. A typical
I/O trap routine looks like the following:

  BeginProc VfooD_Trap_Data
    Dispatch_Byte_IO Fall_Through, VFood_Out_Data
     ...
    (Code for byte input)
    ...
    ret

  VfooD_Out_Data:
    ...
    (Code for byte output)
    ...
    ret
  EndProc VfooD_Trap_Data

Notice the special value Fall_Through that instructs the Dispatch_Byte_IO
macro that byte input should fall through to the following code. You can
substitute Fall_Through for either the input or output parameter (but not
both) or specify two labels.


20.3  I/O Services

This section presents detailed information on each of the following I/O
services:


  ■   Enable_Global_Trapping

  ■   Disable_Global_Trapping

  ■   Enable_Local_Trapping

  ■   Disable_Local_Trapping

  ■   Install_IO_Handler

  ■   Install_Mult_IO_Handlers

  ■   Simulate_IO



Enable_Global_Trapping, Disable_Global_Trapping
────────────────────────────────────────────────────────────────────────────


Description

These services enable and disable I/O port trapping in every VM. A callback
hook must have been installed during initialization before either of these
services is used.

The global trapping state is by default enabled. When a VM is created, it
will be created with the current global trapping state.


Entry

EDX = I/O port number


Exit

None


Uses

Flags


Enable_Local_Trapping, Disable_Local_Trapping
────────────────────────────────────────────────────────────────────────────


Description

These services enable and disable I/O port trapping in a specific VM. A
callback hook must have been installed during initialization before either
of these services is used.


Entry

EBX = VM handle EDX = I/O port number


Exit

None


Uses

Flags


Install_IO_Handler (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service installs a callback procedure for I/O port trapping and enables
trapping for the specified port in all VM's. Only one procedure may be
installed for each port.

When an I/O callback is installed, the default global trapping state is
enabled. You can disable trapping of a port for every or specific VMs using
the Enable/Disable_Global_Trapping and Enable/Disable_Local_Trapping
services.


Entry

ESI = Address of procedure to call EDX = I/O port


Exit

If carry set then  ERROR: Port already hooked by another device or  unable
to hook any more ports (out of hooks) else  Port hooked successfully


Uses

Flags


Callback

EBX = Current VM handle ECX = Type of I/O EDX = Port number EBP -> Client
register structure

If output then  EAX/AX/AL = Data output to port els e (input)  Callback
procedure must return EAX/AX/AL for data input from  port


Install_Mult_IO_Handlers (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service makes repeated calls to the Install_IO_Handler service with the
entries in a table built using macros as follows:

Begin_Vxd_IO_Table Table_Name  Vxd_IO <port#>, <procedure name>     ...
Vxd_IO <port #>, <procedure name>  Vxd_IO <port #>, <procedure name>
End_Vxd_IO_Table Table_Name


Entry

EDI = Address of VxD_IO_Table


Exit

If carry set then  ERROR: One or more ports already hooked by another device
or unable to hook any more ports (out of hooks)  EDX = Number of port that
could not be hooked  else  Ports hooked successfully


Uses

Flags


Callback

EAX = Data for output instructions EBX = Current VM handle ECX = Type of I/O
EDX = Port number EBP -> Client register structure

Callback procedure must return EAX/AX/AL for data input from port


Simulate_IO
────────────────────────────────────────────────────────────────────────────


Description

This service is used to break complex I/O instructions into simpler types of
I/O. An I/O handler should jump to this service using VMMjmp Simulate_IO
whenever the handler is called with a type of I/O that it does not directly
support. A typical I/O trap handler would start with code similar to the
following:

  Sample_IO_Handler:
    cmp ecx, Byte_Output
    je SHORT SIH_Simulate_Output
    jb SHORT SIH_Simulate_Input
    VMMjmp Simulate_IO

Since byte input is 0 and byte output is 4, a single compare can be used to
determine if the I/O is byte input, output, or not supported. When
Simulate_IO is invoked, it will break the I/O into simpler I/O types and
recursively call Sample_IO_Handler.

For example, assume Sample_IO_Handler is the I/O trap handler for port 534H.
If it was called with ECX = Word_Output, then it would immediately jump to
the Simulate_IO service. Simulate_IO would then break the I/O instruction
into byte output to ports 534H and 535H. When Sample_IO_Handler was called
again, it would be able to virtualize the byte output to port 534H. The
output to port 535H would be handled by another port trap routine, or, if
there was not one installed, the output would be reflected directly to
hardware port 535H.

Two macros, Emulate_Non_Byte_IO and Dispatch_Byte_IO, are provided as
convenient ways to invoke this service.

Emulate_Non_Byte_IO is usually the first line of an I/O trap handler. It
simply compares ECX to Byte_Output and, if it is greater, it jumps to the
Simulate_IO service. For example:

  Sample_IO_Handler:
   Emulate_Non_Byte_IO
   (Here ECX will be 0 for byte input or 4 for byte output)

Dispatch_Byte_IO is usually more convenient since it will also jump to the
appropriate code for byte input or output. The macro takes two parameters.
The first parameter specifies the label to jump to for byte input, and the
second specifies the label to jump to for byte output. Either parameter (but
not both) can have the special value Fall_Through, which specifies that the
code to handle that I/O type immediately follows the macro. For example:

  Sample_IO_Handler:
   Dispatch_Byte_IO Fall_Through, <SHORT  SIH_Output>
   (...Code here for handling byte input...)
   ret

  SIH_Output:
   (...Code here for handling byte output...)
   ret

If, for efficiency reasons, you want to provide code to virtualize I/O other
than byte input and output, test for the types that you can handle and then
jump to this service to emulate other types of I/O.

Notice that the entry parameters to this service are identical to the
parameters passed to your I/O trap routine. You should jump to this service
using the VMMjmp macro with all of the registers in the same state as when
your I/O trap routine was called (although you may modify ESI and EDI since
they are not parameters).


Entry

EAX = Data for output instructions EBX = Current VM handle ECX = Type of I/O
(same as passed to I/O trap routine) EDX = I/O port EBP -> Client Register
Structure


Exit

All registers modified. If input, then AX or EAX will contain virtualized
input value.


Uses

EAX, EBX, ECX, EDX, ESI, EDI, Flags






Chapter 21  VM Interrupt and Call Services
────────────────────────────────────────────────────────────────────────────

The VM Interrupt and Call Services supported by enhanced Windows are
described in this chapter in the following order:


  ■   Build_Int_Stack_Frame

  ■   Call_When_Idle

  ■   Call_When_VM_Ints_Enabled

  ■   Disable_VM_Ints

  ■   Enable_VM_Ints

  ■   Get_PM_Int_Type

  ■   Get_V86_Int_Vector

  ■   Get_PM_Int_Vector

  ■   Hook_V86_Int_Chain

  ■   Set_PM_Int_Type

  ■   Set_V86_Int_Vector

  ■   Set_PM_Int_Vector

  ■   Simulate_Far_Call

  ■   Simulate_Far_Jmp

  ■   Simulate_Far_Ret

  ■   Simulate_Far_Ret_N

  ■   Simulate_Int

  ■   Simulate_Iret


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device (VxD) Programming Topics," for general discussions on VM
Interrupts and Call Services.


Build_Int_Stack_Frame
────────────────────────────────────────────────────────────────────────────


Description

This service will save the current CS:IP and flags on the VM's stack and,
then, set the CS:IP to the value passed to the routine. The next time the VM
is entered, the effect will be that an interrupt occurred, directing control
to the procedure specified.

The procedure that is called must do an IRET to return.

Sample code:

  VMMcall Begin_Nest_Exec
        mov      cx, [My_Private_VM_Proc_Segment]
        mov      edx, [My_Private_VM_Proc_Offset]
        VMMcall  Build_Int_Stack_Frame
        VMMcall  Resume_exec
        VMMcall End_Nest_Exec


Entry

CX = Code segment of procedure to call EDX = Offset of procedure to call
(high word must be 0 for 16-bit apps)


Exit

None


Uses

Client_CS, Client_EIP, Client_ESP, Client_Flags, Flags


Call_When_Idle
────────────────────────────────────────────────────────────────────────────


Description

This service is used by devices that want to perform background operations
with the system is "idle". For example, this service is used by the pageswap
device to asynchronously write dirty pages to the swap file. Windows is
considered idle when all VMs have released their time-slice. When the
Windows kernel signals that Windows is idle and all other VMs are idle, the
idle callback procedures will be called. Each callback can either consume
the idle call or pass it on to the next idle callback in the list. Idle
calls should be consumed if the device performs an operation that takes a
significant amount of time. For example, if the pageswap device writes a
dirty page to the swap file then it will consume the idle call to prevent
sluggish performance.


Entry

ESI -> Procedure to call when all VMs idle


Exit

If carry clear then  Callback procedure installed else  Error: Could not
install call-back procedure


Uses

Flags


Callback

EBX = System VM Handle (current VM is always the system VM) EBP -> Client
register structure Return with carry SET to pass the call to next handler
Return with carry CLEAR to consume the callback and indicate Sys VM is not
idle. Callback procedure can modify EAX, EBX, ECX, EDX, ESI, EDI, and Flags.



Call_When_VM_Ints_Enabled
────────────────────────────────────────────────────────────────────────────


Description

If a VxD needs to be called when interrupts are enabled, it can use this
service to be notified when the VM enables interrupts. If the current VM's
interrupts are already enabled when this service is called, your callback
procedure will be called immediately.

It is usually more convenient to use the Call_Priority_VM_event service
instead of calling this service directly. However, this service is faster.


Entry

EDX = Reference data ESI = Offset of procedure to call


Exit

None


Uses

Client_Flags, Flags


Callback

EBX = Handle of current VM EDX = Reference data passed to this service EBP
-> Client register structure Called procedure may destroy EAX, EBX, ECX,
EDX, ESI, EDI, and Flags


Disable_VM_Ints
────────────────────────────────────────────────────────────────────────────


Description

This service will disable interrupts during VM execution for the current
virtual machine. This has the same effect as the VM executing a CLI
instruction.


Entry

None


Exit

None


Uses

Flags


Enable_VM_Ints
────────────────────────────────────────────────────────────────────────────


Description

This service will enable interrupts during VM execution for the current
virtual machine. This has the same effect as the VM executing an STI
instruction. This service allows events scheduled using the
Call_When_Ints_Enabled or Call_Proirity_VM_Event services to be serviced.
Note, however, that they will not be serviced immediately. They will be
serviced at event time. This means that VxDs calling this service do not
need to worry about the VM's state changing during this call.


Entry

None


Exit

None


Uses

Flags


Get_PM_Int_Type
────────────────────────────────────────────────────────────────────────────


Description

This service is used to determine if a PM interrupt vector is an interrupt
gate or trap gate type interrupt. Interrupt gate interrupts clear the
interrupt flag bit to disable interrupts when the interrupt occurs. Trap
gate interrupts don't modify the interrupt bit. All PM interrupts default to
the trap gate type, but VxD's such as VPICD need to change some of them to
interrupt gates so that hardware interrupts disable interrupts. Other
software interrupts such as INT 21h are left as trap gates, so that their
handlers don't need to execute an STI and thus avoid the extra overhead of
an unnecessary ring transition.


Entry

EAX = Interrupt number


Exit

EDX = 0 if vector is a trap gate (interrupt flag is not changed)  0 if
vector is an interupt gate (interrupt flag is cleared)


Uses

EDX, Flags


Get_V86_Int_Vector, Get_PM_Int_Vector
────────────────────────────────────────────────────────────────────────────


Description

These services return the current VM's interrupt vector for the mode
specified. For V86 mode, this is the DWORD located in the real mode
interrupt vector. A PM interrupt vector table is maintained by the VMM for
every virtual machine.

Notice that for PM interrupts, a return with ZF set indicates that the
interrupt has not been hooked. The CS:EIP returned will point to a PM
callback break point. The break point will invoke code that reflects the
interrupt to V86 mode. VxDs can chain to this break point just as they would
any other protected mode address (using Simulate_Far_Jmp or
Build_Int_Stack_Frame) although it is also acceptable to reflect the
interrupt to V86 immediately for optimal performance.


Entry

EAX = Interrupt number


Exit

CX = CS in vector (high word zero) EDX = EIP in interrupt vector (for V86
mode and 16-bit protected mode programs the high word will be zero) For PM
vectors, if interrupt vector points to reflection code (default) then  Zero
flag is set  else  Zero flag is clear


Uses

ECX, EDX, Flags


Hook_V86_Int_Chain (Initialization only)
────────────────────────────────────────────────────────────────────────────

Description

These services are used to monitor software interrupts and simulated
hardware interrupts in Virtual-8086 mode. More than one VxD is allowed to
hook an interrupt. The last interrupt hook will be the first one called.
Every interrupt hook can either service the interrupt or allow the interrupt
to be reflected to the next handler in the chain. If no interrupt hook
procedure consumes the interrupt, then it will be reflected to the virtual
machine.

To consume an interrupt, a hook procedure must return with the Carry flag
clear. If the Carry flag is set when an interrupt hook returns, then the
interrupt will be passed on to the next handler in the chain or, if the end
of the chain is reached, reflected to the current virtual machine.

If a VxD calls the Simulate_Int service, then all interrupt chain hooks will
be called before the interrupt is reflected into the virtual machine.
Simulated hardware interrupts will also be routed through the interrupt
hooks. Therefore, your code should not assume that the VM has just executed
a software interrupt instruction.

Notice that there is no corresponding service to hook Protected Mode
interrupts. PM interrupts must be hooked by hooking the interrupt vector.
This service is faster than hooking the V86 interrupt vector since the VMM
must intercept every V86 interrupt. This prevents a switch to V86 mode and
then back to Ring 0 for every V86 interrupt hook.


Example

Windows running in enhanced mode supports an API using software interrupt
2FH. The code to handle the Release Time-Slice API looks like this:

  Win386_Partial_API_initialization:
   mov      eax, 2fh
   mov      esi, OFFSET32 Win386_Partial_API_Hook
   VMMcall  Hook_V86_Int_Chain
   clc
   ret

  Win386_Partial_API_Hook
   cmp      [epb.Client_AX], 1680h
   je       SHORT W386_PA_Our_Call
   stc
   ret
  W386_PA_Our_Call:
   VMMcall  Release_Time_Slice
   clc
   ret

When Win386_Partial_API_Hook is called, it checks for 1680H in the VM's AX
register. If Client_AX <> 1680H, then it returns with Carry set, and the
interrupt will be reflected to the next handler in the interrupt chain.
However, if Client_AX = 1680H, then it releases the current virtual
machine's time-slice and consumes the interrupt by returning with Carry
clear.


Entry

EAX = Interrupt # ESI Procedure to call


Exit

If carry set then   ERROR: Invalid interrupt number else   Interrupt hook
installed


Uses

Flags


Callback

EAX = Interrupt # EBX = Current VM handle EBP -> Client register structure

If the callback procedure returns with carry clear then   The interrupt is
NOT passed to the next interrupt hook else (if carry set)   The interrupt is
passed to the next interrupt hook


Set_PM_Int_Type
────────────────────────────────────────────────────────────────────────────


Description

This service is used to specify if a PM interrupt vector should either be an
interrupt gate or trap gate type interrupt. Interrupt gate interrupts clear
the interrupt flag bit to disable interrupts when the interrupt occurs. Trap
gate interrupts don't modify the interrupt bit.

All PM interrupts default to the trap gate type, but VxD's such as VPICD
need to change some of them to interrupt gates so that hardware interrupts
disable interrupts.

Other software interrupts such as INT 21H are left as trap gates, so that
their handlers don't need to execute an STI and thus avoid the extra
overhead of an unnecessary ring transition.


Entry

EAX = Interrupt number EDX = 0, if vector is a trap gate (interrupt flag is
not changed)   0, if vector is an interupt gate (interrupt flag is cleared)



Exit

None


Uses

Flags


Set_V86_Int_Vector, Set_PM_Int_Vector
────────────────────────────────────────────────────────────────────────────


Description

This service sets the current interrupt vector for the mode specified. If a
VxD calls Set_xxx_Int_Vector before the Sys_VM_Int control call is made,
then the installed handler will become part of the default interrupt vector
table. In other words, every VM will be created with interrupt vectors set
during enhanced Windows initialization. If this service is called after
Sys_VM_Init, then the handler will only be installed in the current virtual
machine.


Entry

EAX = Interrupt number CX = CS to set into vector EDX = EIP to set interrupt
vector (for V86 mode and 16-bit protected mode programs the   `1`high word
should be zero)


Exit

None


Uses

Flags


Simulate_Far_Call
────────────────────────────────────────────────────────────────────────────


Description

This service places the current VM's CS:IP on the VM's stack and puts the
CS:IP specified in CX:EDX in the client frame. The next time the VM is
executed, it will be as if a FAR call had been inserted in the VM's
instruction stream.

(This figure may be found in the printed book).


Entry

CX = Segment of procedure to call EDX = Offset of procedure to call (high
word 0 if 16-bit application)


Exit

Old Client_CS:(E)IP on stack. Specified CS:EIP is current CS:EIP.


Uses

Client_CS, Client_EIP, Client_SP, Flags ;


Simulate_Far_Jmp
────────────────────────────────────────────────────────────────────────────


Description

This service places the specified CS:IP into the VM's CS:IP to simulate a
FAR jmp instruction.


Entry

CX = CS to jump to EDX = EIP to jump to (High word should be zero for 16-bit
or V86 apps)


Exit

None


Uses

Client_EIP, Client_CS, Flags


Simulate_Far_Ret
────────────────────────────────────────────────────────────────────────────


Decsription

This procedure pops the top two WORDs orDWORDs on the current VM's stack
into the client's CS:(E)IP.

(This figure may be found in the printed book).


Entry

None


Exit

None


Uses

Client_CS, Client_EIP, Client_ESP, Flags


Simulate_Far_Ret_N
────────────────────────────────────────────────────────────────────────────


Description

This procedure pops the top two WORDs or DWORDs on the current VM's stack
into the client's CS:(E)IP, then subtracts EAX from the VM's stack pointer.


(This figure may be found in the printed book).


Entry

EAX = Number of bytes to pop after far ret


Exit

None


Uses

Client_CS, Client_EIP, Client_ESP, Flags


Simulate_Int
────────────────────────────────────────────────────────────────────────────


Description

This service is used mainly by the Virtual Programmable Interrupt Controller
Device (VPICD) to simulate hardware interrupts. Most VxD writers will want
to use the Exec_Int service to simulate interrupts.

This service has exactly the same effect as a VM executing an Int nn
instruction. All VxD interrupt chain hooks are called and, if the interrupt
is not consumed by one of these hooks, an IRET frame is built on the VM's
stack. Notice, however, that the VM interrupt code will not be executed
until the enhanced Windows environment returns to the virtual machine. If
you want to execute an interrupt, then you should use the nested execution
services (Exec_Int).

This service is mode sensitive. Therefore, if the VM is currently in V86
mode, then a V86 interrupt will be simulated. Otherwise, a PM interrupt will
be simulated. Since reflecting a PM interrupt may force a mode change to V86
mode, VxD writers must be very careful when calling this service while
running a protected-mode application.


Entry

EAX = Interrupt number


Exit

If Simulate_Int is called while running a PM application and the PM
interrupt vector is not hooked, then the mode is changed to V86.


Uses

Client_CS, Client_EIP, Client_Flags, Flags


Simulate_Iret
────────────────────────────────────────────────────────────────────────────


Description

This service pops the values at the top of the current VM's stack into the
current VM's CS:IP and flags. If the current VM is a 32-bit protected-mode
application, then this service will pop three DWORDs instead of WORDs
(simulate an IRETD).

(This figure may be found in the printed book).


Entry

None


Exit

None


Uses

Client_CS, Client_EIP, Client_ESP, Client_Flags, Flags






Chapter 22  Nested Execution Services
────────────────────────────────────────────────────────────────────────────

These services provide a way for VxDs to call routines in a VM. Notice that
the VxD must make sure that the service being called is in a callable state
(i.e., you must not reenter services that do not expect to be reentered).
The services are described here in alphabetical order.


  ■   Begin_Nest_Exec

  ■   Begin_Nest_V86_Exec

  ■   Begin_Use_Locked_PM_Stack

  ■   End_Nest_Exec

  ■   End_Use_Locked_PM_Stack

  ■   Exec_Stack

  ■   Exec_VxD_Int

  ■   Restore_Client_State

  ■   Resume_Exec

  ■   Save_Client_State

  ■   Set_PM_Exec_Mode

  ■   Set_V86_Exec_Mode



Begin_Nest_Exec
────────────────────────────────────────────────────────────────────────────


Description

This service is used by VxDs that need to call software in a VM.

For example:

  VMMcall Begin_Nest_Exec   ; Start nested execution
   mov     [ebp.Client_AH], 30h  ; 30h = Get MS-DOS Version #
   mov     eax, 21h    ; Execute an Int 21h in the
   VMMcall Exec_Int    ; current VM to call MS-DOS
   VMMcall End_Nest_Exec   ; End of nested exec calls

This will make the MS-DOS Get_Version call. The version will be in the
Client_AH and Client_AL registers.

This service only works for the current VM. The VM registers changed by the
call Will be changes in the VM. If you want to save and restore a VM's
registers you should use the Save_Client_ State and Restore_Client_State
services or the Push_Client_State and Pop_Client_State macros documneted
under Save_Client_State and Restore_Client_State.

You may execute any number of interrupts between a Begin/End_Nest_Exec pair.
For example the following is valid:

  Push_Client_State
      VMMcall Begin_Nest_Exec
      ...
      VMMcall Exec_Int
      ...
      VMMcall Exec_Int
      ...
      VMMcall Simulate_Far_Call
      VMMcall Resume_Exec
      ...
      VMMcall Exec_Int
      VMMcall End_Nest_Exec
      Pop_Client_State

This service will force the VM into protected-mode execution if there is a
protected-mode application running in the current VM. If there is no
protected mode application, then the VM will remain in V86 mode. When
End_Nest_Exec is called, the VM will be returned to whatever mode it was in
when Begin_Nest_Exec was called. For more information on what is entailed in
a mode switch refer to the documentation for Set_PM_Exec_Mode and
Set_V86_Exec_Mode later in this chapter.

If there is a protected-mode application in the current VM, then this
service will automatically switch the VM to the locked PM stack (and
End_Nest_Exec will switch it back). This allows most devices to change
execution modes without worrying about demand paging issues.


Entry

None


Exit

Client_CS:IP contains a break point (used by nested exec services).

If a protected mode application is running then  VM execution mode is
protected mode else  VM execution mode is Virtual-8086 mode Exec_Int and
Resume_Exec services may be called.


Uses

Client_CS, Client_IP, Flags


Begin_Nest_V86_Exec
────────────────────────────────────────────────────────────────────────────


Description

This service will set the the current VM in Virtual-8086 mode and prepare
the VM for nested execution. This service is normally used by VxDs that want
to convert protected mode calls into V86 calls. For example, the DOSMGR
device uses this call to map INT 21H MS-DOS calls issued from protected mode
programs into Virtual-8086 mode MS-DOS calls.

This call, like Begin_Nest_Exec, saves the current execution mode of the
virtual machine (either V86 or PM), and End_Nest_Exec will restore the mode.



Entry

None


Exit

Client_CS:IP contains a break point (used by nested exec services) VM is in
Virtual 8086 mode. Exec_Int and Resume_Exec services may be called.


USES

Client_CS, Client_IP, Flags


Begin_Use_Locked_PM_Stack
────────────────────────────────────────────────────────────────────────────


Description

This service is used by VxDs that need to ensure that a protectedmode
program is running on a stack that will not be demand paged. Most devices
can rely on Begin_Nest_Exec to switch stacks automatically, and so this
service is only important for devices, such as the Virtual Programmable
Interrupt Controller Device (VPICD), which explicitly changes the execution
mode of a VM.

A call to this service must be followed by a call to
End_Use_Locked_PM_Stack. Note that this service may be called repeatedly,
but only the first call will switch stacks. Subsequent calls will increment
a counter but remain on the current locked stack.


Entry

Current execution mode of VM must be protected mode (VMStat_PM_Exec status
bit must be set).


Exit

If locked stack not already in use then  Client's SS:SP will be changed to
locked protected mode stack else  Client's SS:SP will be unchanged


Uses

Flags


End_Nest_Exec
────────────────────────────────────────────────────────────────────────────


Description

This service must be called after a call to Begin_Nest_Exec. A VxD must
never return to the VMM while still in nested execution. If Begin_Nest_Exec
changed the execution mode of the VM then this service will restore it to
the previous mode. Notice that this service will not restore the client's
registers (except CS:IP) to the values they were when Begin_Nest_Exec was
called. If you need to preserve the VM's registers, you must use the
Push_Client_State and Pop_Client_State macros.


Entry

None


Exit

VM execution mode restored to previous execution mode (before
Begin_Nest_Exec was called) Client's original CS:IP restored


Uses

Client_CS, Client_IP, Flags


End_Use_Locked_PM_Stack
────────────────────────────────────────────────────────────────────────────


Description

This service must be called once for every call made to
Begin_Use_Locked_PM_Stack. It will decrement the locked stack use counter
and, if it is decremented to zero then it will switch the VM back to its
original SS:SP.


Entry

None


Exit

If locked stack count decremented to 0 then  Client's SS:SP will be restored
to original values before  Begin_Use_Locked_PM_Stack was called. else
Client's SS:SP will be unchanged


Uses

Flags


Exec_Int
────────────────────────────────────────────────────────────────────────────


Description

You must call Begin_Nest_Exec or Begin_Nest_V86_Exec before calling this
service. It may be called any number of times between a Begin_Nest_Exec and
End_Nest_Exec pair.

This service simulates an interrupt and then resumes VM execution. It has
exactly the same effect as calling:

  mov     eax, (Int #)
      VMMcall Simulate_Int
      VMMcall Resume_Exec

Since most nested execution calls simulate interrupts, this service is
provided for convenience. See Resume_Exec later in this chapter for more
details on how this service is used.


Entry

EAX = # of interrupt to execute


Exit

Interrupt has been executed


Uses

Flags


Exec_VxD_Int
────────────────────────────────────────────────────────────────────────────


Description

This service is used by virtual devices to call MS-DOS or BIOS services as
though they were an application program. For example, the following code
gets the current MS-DOS version:

  mov     ax, 3000h
      push    DWORD PTR 21h
      VMMcall Exec_VxD_Int
      (AL = Major MS-DOS version, AL = Minor MS-DOS version)

All MS-DOS and BIOS calls that are supported in protected-mode programs will
be supported by this service. The VM's registers and flags will not be
changed by this service so there is no need for the caller to save and
restore the client register structure. The interrupt number on the stack
will be removed by this service so the caller should not add four to ESP
after calling this service.

To make calling this service easier, a macro called VxDint is defined in
VMM.INC as follows:

  VxDint  MACRO   Int_Number
        push    Int_Number
        VMMcall Exec_VxD_Int
        ENDM

This service makes it possible to write code in a VxD that is very similar
to real mode code. For example, below is the code that opens a file named
"FOO.TXT" and reads the first 100 bytes:

  VxD_DATA_SEG
   Foo_File_Name   db "FOO.TXT", 0
   Read_Buffer     db 100 dup (?)
  VxD_DATA_ENDS

  VxD_CODE_SEG
   BeginProc Sample_File_Read
     mov     ax, 3D00h   ; Open file with handle
     mov     edx, OFFSET32 Foo_File_Name  ; DS:EDX - File name
      VxDint  21h     ; Call MS-DOS
     jc      Error   ; If carry then error
          ; else AX = File handle
      mov     bx, ax    ; BX = File handle
     mov     ecx, 100    ; Read 100 bytes
     mov     edx, OFFSET32 Read_Buffer ; Into this buffer
      mov     ah, 3Fh    ; MS-DOS Read
     VxDint  21h     ; Call MS-DOS
      jc      Error   ; Error if carry else
          ; EAX = # bytes read
     (Do stuff with the data here)

  EndProc Sample_File_Read
  VxD_CODE_ENDS


Comments

Interrupts will only be routed through VxD interrupt hooks. They will bypass
any hook the application has installed in protected mode. This may be a
problem, for example, if an application hooks INT 21h to watch for a file
open and, then, a VxD uses this service to open a file (the application
would not see the file open). Do not use this service until the
Init_Complete control call is made.

Do not change DS or ES before calling this service. You should always use
the ring 0 linear address of the data instead of changing the selector
value. This may require using the _SelectorMapFlat service to determine the
base of a selector.

Do not call services that will change DS or ES. Mappers should return valid
pointers without changing the segment register value, but calls that
explicitly change the DS or ES selectors should never be called. For
example, if a call returns a pointer in DS:(E)DX then this would be OK to
call since the mapper would convert the ponter to use the ring 0 linear
address in EDX without modifying DS. However, if a service returns a
selector only then you should not use Exec_VxD_Int to call it. This can
normally be made to work by using code similar to the following:

  Push_Client_State
    VMMcall Begin_Nest_(V86_)Exec
    . . .
    (Fiddle with client registers)
    . . .
    VMMcall Exec_Int
    . . .
    (Get segments/selectors)
    . . .
    VMMcall End_Nest_Exec
    Pop_Client_State


Entry

DWORD at [ESP+4] is number of interrupt to execute


Exit

All registers and flags modified by interrupt will be changed. The interrupt
number on the stack will have been removed.


Uses

All registers and flags modified by interrupt will be changed.


Restore_Client_State
────────────────────────────────────────────────────────────────────────────


Description

This service restores a VM execution state that was saved using the
Save_Client_State service. If the client state was saved using the
Push_Client_State macro then you should use Pop_Client_State to restore the
VM's execution state. The Pop_Client_State macro looks like:

  Pop_Client_State MACRO
       push    esi
       lea     esi, [esp+4]
       VMMcall Restore_Client_State
       pop     esi
       add     esp, SIZE Client_Reg_Struc
      ENDM

Notice that this service can have side effects if it is not used carefully.
For instance, it will change modes from V86 to protected mode or from
protected to V86 mode if the state being restored is in a different
execution mode from the current one. Also, it may change the state of the
current virtual machine's interrupt flag and so it may cause callbacks to
events scheduled through the Call_When_VM_Ints_Enabled or
Call_Priority_VM_Event services.


Entry

ESI -> Buffer


Exit

VM execution state is restored


Uses

Flags


Resume_Exec
────────────────────────────────────────────────────────────────────────────


Description

You must call Begin_Nest_Exec or Begin_Nest_V86_Exec before calling this
service. It may be called any number of times between a Begin_Nest_Exec and
End_Nest_Exec pair.

This service immediately executes the current VM. When the VM returns to the
same point it was at when Begin_ Nest_Exec was called, this service will
return. The following example:

  Push_Client_State
   VMMcall  Begin_Nest_Exec
   mov  cx, [Target_CS]
   mov  eax, [Target_CS_EIP]
   VMMcall  Simulate_Far_Call
   VMMcall Resume_Exec
    (Examine results returnd in Client registers)
   VMMcall End_Nest_Exec
  Pop_Client_State

will return when the called procedure returns.The following code will
process any outstanding events and immediately return:

  VMMcall Begin_Nest_Exec
   VMMcall Resume_Exec
   VMMcall End_Nest_Exec

Since the Resume_Exec resumes execution at the same point that
Begin_Nest_Exec was called it will return immediately.

This service is also useful for VxDs that must wait for an external event
(such as a hardware interrupt) to occur before returning to the VM. Since
Resume_Exec allows outstanding events to be processed, simulated harware
interrupts can be sent to the VM while waiting:

  (Push_Client_State is not needed)
   VMMcall Begin_Nest_Exec
   Wait_Loop:
   test  [My_Status], Done
   je  Exit_My_Wait_Loop
   VMMcall Resume_Exec
   VMMcall Release_Time_Slice
   jmp  My_Wait_Loop
   Exit_Wait_Loop:
   VMMcall  End_Nest_Exec
   (Pop_Client_State is not needed)

Notice that you do not need to save and restore the client registers in this
loop since simulated hardware interrupts and events will not modify the
client registers. You should only use the Push/ Pop_Client_State macros when
your VxD code explicitly calls code in a VM or directly modifies any client
register.

This service and Exec_Int may be called multiple times in between calls to
Begin/End nest exec. For example the following code is valid:

  Push_Client_State
    VMMcall Begin_Nest_Exec
    mov eax, (Int #)
    VMMcall Exec_Int
    mov cx, [Target_CS]
    mov eax, [Target_CS_EIP]
    VMMcall Simulate_Far_Call
    VMMcall Resume_Exec
    VMMcall End_Nest_Exec
    Pop_Client_State

Since events are processed when Resume_Exec (or Exec_Int) is called, a task
switch may occur.


Entry

None


Exit

None


Uses

Flags


Save_Client_State
────────────────────────────────────────────────────────────────────────────


Description

This service will copy the contents of the current VM's Client Register
Structure to the specified buffer. The buffer must be the size of the
structure named Client_Reg_Struc which is defined in VMM.INC. The saved
state can later be restored by calling Restore_Client_State.

Most of the time, it is easier to use the Push_Client_State macro than to
call this service directly. Push_Client_State copies the client's state onto
the protected mode stack. The macro code is as follows:

  Push_Client_State MACRO
    sub     esp, SIZE Client_Reg_Struc
    push    edi
    lea     edi, [esp+4]
    VMMcall Save_Client_State
    pop     edi
    ENDM

As you can see, this macro will reserve space on the caller's stack for the
buffer. You must use the Pop_Client_State macro to get rid of the contents
saved on your stack. The macro will not change any registers.

This service is typically used by VxDs that need to make calls to code in a
virtual machine that are unrelated to the current VM's thread of execution.
For example, the demand paging device (PageSwap) does the following:

  Push_Client_State
      VMMcall Begin_Nest_Exec
      . . .
      (Perform disk I/O)
      . . .
      VMMcall End_Nest_Exec
      Pop_Client_State

Notice that the Push_Client_State macro is placed before the call to
Begin_Nest_Exec and the Pop_Client_State macro is after the call to
End_Nest_Exec. Any other combination would probably crash enhanced Windows.


────────────────────────────────────────────────────────────────────────────
WARNING

Always use this service to save the client state. Don't just copy the VM's
client register structure and later copy it back as this will almost
certianly cause enhanced Windows to hang or crash.
────────────────────────────────────────────────────────────────────────────


Entry

EDI -> Buffer


Exit

Buffer contains a copy of the current VM's client register structure


Uses

Flags


Set_PM_Exec_Mode
────────────────────────────────────────────────────────────────────────────


Description

This service forces the current VM into protected mode. Most devices will
want to use Begin_Nest_Exec instead of this service.

Changing the execution mode of a VM will not change the VM's EAX, EBX, ECX,
EDX, ESI, EDI, and EBP registers or most flags. The VM flag and IOPL flags
will change. DS, ES, FS, GS, SS, ESP, CS, and EIP will be restored to the
previous values for protected mode.

If the current VM is already in protected mode, then this service has no
effect.


Entry

None


Exit

VM is in PM execution mode


Uses

Flags


Set_V86_Exec_Mode
────────────────────────────────────────────────────────────────────────────


Description

This service forces the current VM into V86 mode. Most VxDs will want to use
Begin_Nest_V86_Exec instead of this service.

Changing the execution mode of a VM will not change the VM's EAX, EBX, ECX,
EDX, ESI, EDI, and EBP registers or most flags. The VM flag and IOPL flags
will change. DS, ES, FS, GS, SS, ESP, CS, and EIP will be restored to the
previous values for V86 mode.

If the current VM is already in V86 mode, then this service has no effect.


Entry

None


Exit

VM is in V86 execution mode


Uses

Flags






Chapter 23  Break Point and Callback Services
────────────────────────────────────────────────────────────────────────────

The services described in this chapter are used to handle breakpoint and
callback procedures.

The discussion of these services is presented in the following order:


  ■   Allocate_V86_Call_Back

  ■   Allocate_PM_Call_Back

  ■   Call_When_Idle

  ■   Call_When_VM_Returns

  ■   Install_V86_Break_Point

  ■   Remove_V86_Break_Point


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


Allocate_V86_Call_Back, Allocate_PM_Call_Back
────────────────────────────────────────────────────────────────────────────


Description

A V86 callback is used to transition from V86 mode into a protected mode
VxD. The callback is a SEGMENT:OFFSET that, when executed by a V86 machine,
will cause a procedure in a virtual device to be called.

A PM callback is used to transition from a protected-mode application to a
VxD. The callback is a SELECTOR:OFFSET that, when executed, will cause a
procedure in a virtual device to be called.

These services are typically used by devices that need to be called by
software running in a virtual machine. When the VM software calls the
callback address, the VxD gets control and can service the VM's request.

  Initialization:
    mov edx, My_Ref_Data
    mov esi, OFFSET33 My_API_Procedure
    VMMcall Allocate_V86_Call_Back
    mov [My_V86_Call_Back], eax
    mov [ebp.Client_DI], ax
    shr eax, 16
    mov [ebp.Client_ES], ax
   ret

  My_API_Procedure:
   . . . (Do something here) . . .
   VMMcall Simulate_Far_Ret
   ret


Entry

EDX = Reference data (any DWORD) ESI = Procedure to call


Exit

EAX = CS:IP of V86 callback address


Uses

EAX, Flags


Callback

EBX = Current VM handle EDX = Reference data EBP -> Current VM's client
register structure


Call_When_Idle
────────────────────────────────────────────────────────────────────────────


Description

This service is used by devices that want to perform background operations
with the system is "idle". For example, this service is used by the pageswap
device to asynchronously write dirty pages to the backing store. Windows is
considered idle when all VMs have released their time-slice. When the
Windows kernel signals that Windows is idle and all other VMs are idle, the
idle callback procedures will be called. Each callback can either consume
the idle call or pass it on to the next idle callback in the list. Idle
calls should be consumed if the device performs an operation that takes a
significant amount of time. For example, if the pageswap device writes a
dirty page to the backing store then it will consume the idle call to
prevent slugish performance.


Entry

ESI -> Procedure to call when all VMs idle


Exit

If carry clear then  Callback procedure installed else  Error: Could not
install call-back procedure


Uses

Flags


Callback

EBX = System VM Handle (current VM is always the system VM) EBP -> Client
register structure Return with carry SET to pass the call to next handler
Return with carry CLEAR to consume the callback and indicate Sys VM is not
idle. Callback procedure can modify EAX, EBX, ECX, EDX, ESI, EDI, and Flags.



Call_When_VM_Returns
────────────────────────────────────────────────────────────────────────────


Description

This service is normally used to watch the "back end" of a software
interrupt. For example, assume that the procedure I16_Hook has been placed
in the V86 interrupt chain (using the Hook_V86_Int_Chain service). If the
procedure wants to look at the return value from INT 16H, it would use the
following code:

  I16_Hook:
    xor eax, eax    ; No time-out
    mov esi, OFFSET32 I16_Return   ; Call this when iret executed
    VMMcall Call_When_VM_Returns    ; Hook the return
    stc     ; Reflect to next int handler
    ret

  I16_Return:
    (Examine results of Int 16h)
    ret

This service actually replaces the client's CS:IP with a callback. Since at
the point I16_Hook is executed the interrupt IRET frame has not yet been
built on the client's stack, the callback will be pushed on the client's
IRET frame. When the VM executes an IRET to return from the interrupt, the
callback break point will be executed and control will be transferred to
I16_Return. This service will take care of restoring the client's CS:IP
registers to their original value.


Entry

EAX = Milliseconds until time-out (0 if no time-out)  If negative value then
callback will be called for both time out AND return (unless return before
time-out). EDX = Reference data ESI = Address of procedure to call


Exit

Client_CS:EIP replaced with callback address


Uses

Client_CS, Client_EIP, Flags


Callback

EBX = Current VM handle EDX = Reference data EBP -> Client register
structure

If carry set then  Time-out occurred before VM returned else  Client_CS:IP
restored to original value  VM returned and executed break point  If
time-out value specified was negative then  If Zero flag set then  Time-out
DID occur. Second call to this callback  else  Time-out did not and will not
occur.


Install_V86_Break_Point
────────────────────────────────────────────────────────────────────────────


Description

This service is used to patch V86 code in a VM. It is primarily used by the
DOSMGR device to place patches in the MS-DOS BIOS. Most VxD will have no use
for this service. A good example of a typical use for this service is the
Windows XMS virtual device. Since there is already a real mode XMS driver
when the enhanced Windows environment starts, the virtual XMS device must
place a V86 break point at the real XMS driver entry point so that it can
intercept all XMS calls.

This service places an enhanced Windows V86 break point instruction at the
specified SEGMENT:OFFSET in the current virtual machine. V86 break points
will normally be placed in global VM memory during device initialization.
V86 break points must be placed only in RAM that will have a constant linear
address (they cannot move or be placed in ROM).

When a VM executes the break point, control will be passed to the VxD that
installed it. The client's (VM's) CS:IP will still point to the break point
that caused the fault. Therefore, the virtual device must change the CS:IP
or else the break point will be executed again when the VxD environment
returns to the VM. In the case of the virtual XMS device, it would call
Simulate_Far_Ret to return to the code that called the XMS driver. Other
devices may want to simulate the instruction that was patched out and
increment the IP past the patch, jump to another CS:IP using
Simulate_Far_Jmp, or return from an interrupt handler using Simulate_Iret.

If a particular V86 break point is no longer needed, then the VxD should
call Remove_V86_Break_Point. Also, any break points that are placed in
global V86 code (code loaded before Windows/386 was loaded) must be removed
at System_Exit time.

────────────────────────────────────────────────────────────────────────────
NOTE

The segment used to install a V86 break point must be the code segment the
virtual machine will use when it executes the code that is being patched.
For example, if you place a patch at 0100:0010 and the virtual machine hits
the break point at 0101:0000h (which is the same linear address as
0100:0010), then an error will occur even though the VM executed a valid
break point.
────────────────────────────────────────────────────────────────────────────


Entry

EAX  = CS:IP EDX = Reference data (any DWORD) ESI = Offset of procedure to
call


Exit:

  If carry set then
      Could not install break point
   else
      V86 break point successfully installed


Uses

Flags


Callback

EAX = Client CS:IP that faulted EBX = Handle of current VM EDX = Reference
data ESI = Linear address of break point (CS << 4 + IP)  EBP -> Client
register structure


Remove_V86_Break_Point
────────────────────────────────────────────────────────────────────────────


Description

This service is used to remove a V86 break point that was installed using
the Install_V86_Break_Point service. It will restore the original contents
of the memory automatically.


Entry

EAX = CS:IP of break point to remove


Exit

  If carry set then
      ERROR: Not a valid V86 break point
   else
      Previous value restored at break point SEG:OFFSET


Uses

Flags






Chapter 24  Primary Scheduler Services
────────────────────────────────────────────────────────────────────────────

Each virtual machine is a separate task in the enhanced Windows environment.
There are several services that are used to control the scheduling of
virtual machines.

Every VM has an execution priority. The VM with the highest execution
priority is allowed to run unless the VM is suspended or is blocked waiting
for a critical section to be freed. A VM's execution priority can be raised
or lowered using the Adjust_Execution_Priority service.

A VxD can force a particular virtual machine to run by boosting its
execution priority. However, VxD authors should take care when changing the
priority of a VM since doing so can radically effect the behavior of the
Windows time-slicer.

To allow the mutual exclusion of non-reentrant code, the scheduler supports
a single critical section. The current VM can claim the critical section at
any time by calling Begin_Critical_Section. If another VM owns the critical
section, then the current VM will block until the critical section is
released. Once the critical section is claimed, the VM's execution priority
is boosted. However, VMs with higher priorities will still be allowed to
execute. Normally, VMs are only boosted higher than the critical section
priority when a hardware interrupt is simulated.

A VM may be suspended if it is not in a critical section. However, the
system VM can never be suspended. A suspended VM will never be scheduled,
regardless of its execution priority, until it is resumed.

An important thing to keep in mind is that since the enhanced Windows
environment is a single-threaded operating system, you do not have to be
concerned with a task switch from within a procedure. For example, another
VM will not be scheduled while in a virtual device I/O trap handler. Task
switches take place when a VxD makes an explicit call to the scheduler
(i.e., End_Critical_Section) or at event processing time. Notice that since
events are processed when Resume_Exec or Exec_Int are called, a task switch
may occur while performing nested VM execution. Also, touching or locking
unlocked demand-paged memory may cause a task-switch. In summary, the times
when a task switch may occur are as follows:


  ■   Explicit calls to the scheduler

  ■   Performing nested execution (Resume_Exec or Exec_Int)

  ■   Touching or locking demand-paged memory


The discussion of services providing support for the Primary Scheduler is
presented in the following order:


  ■   Adjust_Exec_Priority

  ■   Begin_Critical_Section

  ■   Call_When_Not_Critical

  ■   Call_When_Task_Switched

  ■   Claim_Critical_Section

  ■   Create_Semaphore

  ■   Destroy_Semaphore,D

  ■   End_Crit_And_Suspend

  ■   End_Critical_Section

  ■   Get_Crit_Section_Status

  ■   No_Fail_Resume_VM

  ■   Nuke_VM

  ■   Release_Critical_Section

  ■   Resume_VM

  ■   Signal_Semaphore

  ■   Suspend_VM

  ■   Wait_Semaphore


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


Adjust_Exec_Priority
────────────────────────────────────────────────────────────────────────────


Description

This service is used to raise or lower the execution priority of the
specified VM. Since the non-suspended VM with the highest execution priority
is always the current VM, this service will cause a task switch under two
circumstances:


  1.  The execution priority of the current VM is lowered (EAX is negative),
      and there is another VM with a higher priority that is not suspended.

  2.  The execution priority of a non-suspended VM which is not the current
      VM is raised (EAX is positive) higher than the current VM's execution
      priority.


Note that even if the current VM is in a critical section, a task switch
will still occur if the priority of another non-suspended VM is raised
higher than the current VM's priority. However, this will only happen when a
VM is given a time-critical boost, for example, to simulate a hardware
interrupt. There are equates defined in VMM.INC that should be used when
adjusting a VM's priority. They are listed below in order from lowest to
highest.

╓┌─────────────────────────────────┌─────────────────────────────────────────►
Equate Name                       Description
────────────────────────────────────────────────────────────────────────────
Reserved_Low_Boost                Reserved for use by system.

Cur_Run_VM_Boost                  Time-slice scheduler boosts each VM in
                                  turn by this value to force them to run
                                  for their alloted time-slice.

Low_Pri_Device_Boost              Used by VxDs that need an event to be
                                  processed in a timely fashion but that are
                                  not extremely time critical.

High_Pri_Device_Boost             Time critical operations that should not
                                  circumvent the critical section boost
                                  should use this boost.
Equate Name                       Description
────────────────────────────────────────────────────────────────────────────
                                  should use this boost.

Critical_Section_Boost            VM priority is boosted by this value when

                                  Begin_Critical_Section is called.

Time_Critical_Boost               Events that must be processed even when
                                  another VM is in a critical section should
                                  use this boost. For example, VPICD uses
                                  this when simulating hardware interrupts.

Reserved_High_Boost               Reserved for use by system.



It is often more convienient to call Call_Priority_VM_Event than to call
this service directly.


Entry

EAX  = + or - priority boost (signed long integer) EBX  = VM handle


Exit

None


Uses

Flags


Begin_Critical_Section
────────────────────────────────────────────────────────────────────────────


Description

Use of this service causes the current VM to enter a global critical
section. Only one VM can own the critical section at a time. If a VM calls
this service while another VM owns the critical section, then the current VM
will block until the critical section is released.

The critical section is maintained as a count and so n calls to
Begin_Critical_Section must be followed by n calls to End_Critical_Section
before the VM will leave the critical section.

When the critical section is first claimed, the execution priority of the
current VM is boosted by the Critical_Section_Boost value defined in
VMM.INC. This means that task switches to other VMs will only occur for
time-critical operations such as simulating hardware interrupts.

Critical sections are used for code that must not be entered in more than
one VM. For example, while in MS-DOS, the DOSMGR VxD places the VM in a
critical section. If another VM makes a MS-DOS call, then it will block
until the critical section owner's MS-DOS call completes. However, this
scenario is unlikely since a VM has an extremely high execution priority
while it owns the critical section, and, therefore, other VMs will not run
until the critical section is released. A scenario that would cause a VM to
block  is as follows:

  VM X calls MS-DOS to read a file.
  The DOSMGR calls Begin_Critical_Section for VM X. This raises
    VM X's priority by the Critical_Section_Boost.
  The Virtual Keyboard Device simulates an interrupt to VM Y.
  VM Y is sceduled since it has a higher execution priority
   (simulated interrupts use the Time_Critical_Boost).
  A T&SR program "wakes up" on the keyboard interrupt and calls DOS.
  The DOSMGR calls Begin_Critical_Section for VM Y.
  VM Y blocks since another VM owns the critical section.
  VM X is scheduled since it has the highest exectution priority.
  The MS-DOS read for VM X completes.
  DOSMGR calls End_Critical_Section for VM X. This lowers
    VM X's priority by the Critical_Section_Boost.
  VM Y is un-blocked and scheduled since it has the highest priority.
  VM Y continues execution at the instruction immediately after the
    call to Begin_Critical_Section and executes the MS-DOS call.

Sometimes it is preferable to boost the current VM by the
Time_Critical_Boost value instead of entering a critical section. This
prevents the main thread of execution from running in all but the current VM
but avoids blocking a VM when it is not really necessary.


Entry

None


Exit

None


Uses

Flags


Call_When_Not_Critical
────────────────────────────────────────────────────────────────────────────


Description

This service will call a VxD when the critical section is released. Notice
that it will not execute the callback until the current VM's execution
priority is less than the Critical_Section_Boost even when the current VM is
not in a critical section. This is done because most VxDs that use this
service will want to wait until the critical section is free and no hardware
interrupts are being simulated.

Normally it is more convenient to use the Call_Priority_VM_Event service
than to call this service directly.


Entry

ESI = Address of call-back procedure EDX = Reference data to pass to
callback procedure


Exit

None


Uses

Flags


Callback

EBX = Current VM handle EDX = Reference data EBP -> Client register
structure

Procedure can corrupt EAX, EBX,  ECX, EDX, ESI, EDI, and Flags


Call_When_Task_Switched
────────────────────────────────────────────────────────────────────────────


Description

This service provides a way to be informed each time a different VM is to be
executed. The specified procedure will be called every time a task switch
occurs. Since this is a frequent operation in most environments, this
service should be used sparingly, and the callback procedure should be
optimized for speed.

VxDs must sometimes save the state of a hardware device every time a task
switch occurs and restore the hardware state for the VM that is about to be
run. However, VM events can often be used in place of using this service.


Entry

ESI = Pointer to procedure to call at task switch time


Exit

None


Uses

Flags


Callback

EAX = Handle of VM switching away from (old Cur_VM_Handle)  EBX = Current VM
(just switched to)

Procedure can destroy EAX, EBX, ECX, EDX, ESI, EDI and Flags


Claim_Critical_Section
────────────────────────────────────────────────────────────────────────────


Description

This service will increment the critical section count by the specified
value. It has the same effect as calling Begin_Critical_Section repeatedly
but is faster. Refer to the documentation for Begin_Critical_Section for
more information on the various side effects of entering a critical section.



Entry

ECX = # of times to claim the critical section (0 is valid & ignored)


Exit

None


Uses

Flags


Create_Semaphore
────────────────────────────────────────────────────────────────────────────


Description

Do not use. Reserved for future release.


Destroy_Semaphore
────────────────────────────────────────────────────────────────────────────


Description

Do not use. Reserved for future release.


End_Crit_And_Suspend
────────────────────────────────────────────────────────────────────────────


Description

This service will release the critical section and immediately suspend the
current VM. It is used to block a VM until another event can be processed.
This service is used by the Shell VxD to display Windows dialog boxes using
code similar to this:

  Show_Dialog_Box:
    VMMcall  Get_Crit_Section_Status
   jc  Cant_Do_It!
    VMMcall  Begin_Critical_Section
   mov  eax, Low_Pri_Device_Boost
   VMMcall  Get_Sys_VM_Handle
    mov  ecx, 11b
   mov  edx, OFFSET32 (Dialog_Box_Data_Structure)
    mov  esi, OFFSET32 Show_Dialog_Event
   VMMcall  Call_Priority_VM_Event
    VMMcall  End_Crit_And_Suspend
    jc  Did_Not_Work!
     ; (When End_Crit_And_Suspend returns the dialog box
     ; will have been displayed)

  Show_Dialog_Event:
    (Call Windows to display the dialog box)
    mov ebx, [Handle_Of_VM_That_Called_Show_Dialog_Box]
    VMMcall Resume_VM
    jc  Error!
    ret

The Show_Dialog_Box procedure enters a critical section to prevent the
Call_Priority_VM_Event service from switching to the system VM immediately.
It then calls End_Crit_And_Suspend, which blocks the current VM. The
Show_Dialog_Event procedure runs in the system (Windows) VM and actually
displays the dialog box. When it is finished, it resumes the VM that called
Show_Dialog_Box.

This service must only be called when the critical section has been claimed
once. That is the reason for the initial test of the critical section state
in the Show_Dialog_Box procedure in the sample code.


Entry

None


Exit

If carry set then  ERROR: Could not suspend VM or could not release critical
section (crit claim count != 1) else  Call worked. VM execution restarted by
another VM calling  "Resume_VM".


Uses

Flags


End_Critical_Section
────────────────────────────────────────────────────────────────────────────


Description

This service is used to release the global critical section after a call to
Begin_Critical_Section has been issued. If the critical section ownership
count is decremented to 0, then ownership of the critical section is
released. Since releasing the critical section lowers the execution priority
of the current VM, this service will cause a task switch if a non-suspended
VM has a higher priority.


Entry

None


Exit

None


Uses

Flags


Get_Crit_Section_Status
────────────────────────────────────────────────────────────────────────────


Description

This service returns the critical section claim count in ECX and the owner
of the critical section in EBX. If ECX is 0, then the current VM handle will
be returned in EBX.

If this service returns with the Carry flag set, then the VM is in a
time-critical operation such as a hardware interrupt simulation. (It has an
execution priority = Critical_Section_Boost.)


Entry

None


Exit

EBX = VM handle of current owner (Current VM if ECX = 0) ECX = # of times
critical section claimed If carry set then VM is in a time-critical
operation or critical section.


Uses

Flags


No_Fail_Resume_VM
────────────────────────────────────────────────────────────────────────────


Description

This service is used to resume the execution of a VM that was previously
suspended by a call to Suspend_VM. It differs from the normal Resume_VM
service in that it will never return an error. If theVM can not be resumed,
the scheduler will handle the error condition automatically and the user
will be notified of the problem. The VM will then be resumed when there is
sufficient memory available. A task switch will occur to the resumed VM if
it has a higher priority than the current VM.


Entry

EBX = VM handle


Exit

None


Uses

Flags


Nuke_VM
────────────────────────────────────────────────────────────────────────────


Description

This service is used to close a VM that has not yet terminated normally. It
is usually called by the Shell VxD to close VMs that the user has selected
to terminate using the Window Close option on the VM's system menu.

Needless to say, this service should be used very cautiously.


Entry

EBX = Handle of VM to destroy


Exit

If entry EBX = Current VM handle then  This service will never return (same
as Crash_Cur_VM)  else  If EBX = System VM handle then  This service will
never return (fatal error─crash to DOS)   else  VM has been nuked


Uses

Flags


Release_Critical_Section
────────────────────────────────────────────────────────────────────────────


Description

This service will decrement the critical section count by the specified
value. It has the same effect as calling End_Critical_Section repeatedly but
is faster.


Entry

ECX = # of times to release ownership of critical section (0 valid)


Exit

None


Uses

Flags


Resume_VM
────────────────────────────────────────────────────────────────────────────


Description

This service is used to resume the execution of a VM that was previously
suspended by a call to Suspend_VM. If the suspend count is decremented to 0,
the VM will be placed on the queue of ready processes. A task switch will
occur to the resumed VM if it has a higher priority than the current VM.

It is sometimes not possible to resume a VM. Normally, this is because a VxD
is unable to lock the VM's memory handles. Every VxD is notified when a VM
is resumed and can fail the call. In this case, this service will return
with Carry set, and the VM will remain suspended with a suspend count of 1.



Entry

EBX = VM handle


Exit

If carry clear then  If suspend count decremented to 0 then VM is runnable
else  Error could not resume (Suspend count remains 1)


Uses

Flags


Signal_Semaphore
────────────────────────────────────────────────────────────────────────────


Description

Do not use. Reserved for future release.


Suspend_VM
────────────────────────────────────────────────────────────────────────────


Description

This service will suspend the execution of a specified Virtual Machine. Any
VM, except the system VM, that is not in a critical section can be
suspended. This service will fail if the specified VM is the critical
section owner or the system VM. The system VM can never be suspended.

This service maintains a count that is incremented each time a VM is
suspended. Therefore, if this service is called n times for a given VM,
Resume_VM must be called n times before the VM will be executed.

When a VM is being suspended for the first time (its suspend count is
incremented from 0 to 1), all devices will receive a control call with EAX =
VM_Suspend. Devices may not refuse to suspend a VM. However, VxDs are
allowed to fail the VM_Resume control call. Subsequent calls to Suspend_VM
will not result in a VM_Suspend control call until the VM has been resumed.


When a VM is suspended, the CB_VM_Status field in the control block will
have the VMStat_Suspended bit set. When a VM is suspended, VxDs should not
touch any memory owned by that VM unless the VxD has previously locked the
memory. You may, however, examine or modify the contents of a suspended VM's
control block.


Entry

EBX = VM handle


Exit

If carry flag clear then  VM suspended  else  Error: Could not suspend VM
(VM is in a critical section or  is the system VM)


Uses

Flags


Wait_Semaphore
────────────────────────────────────────────────────────────────────────────


Description

Do not use. Reserved for future release.






Chapter 25  Time-Slice Scheduler Services
────────────────────────────────────────────────────────────────────────────

The enhanced Windows time-slice scheduler is the preemptive multitasking
portion of the scheduler. It relies on time-slice priorities and flags to
determine how much CPU time should be allocated to various virtual machines.


Every VM has a foreground (focus) and a background time-slice priority.
These should be distinguished from a VM's Execution Priority (described in
previous chapter). A VM with the largest Execution Priority will run,
preventing other VMs from executing. The VM with the largest time-slice
priority will run more often than other VMs but it will not necessarily
prevent other VMs from executing.

There are three flags that affect the way the time-slicer schedules virtual
machines: VMStat_Exclusive, VMStat_Background, and
VMStat_High_Pri_Background. These flags are saved in the CB_VM_Status field
of each VM's control block. You may examine these flags but you must never
modify them directly. To change any of the flags, you must call the
Set_Time_Slice_Priority service.

If a VM that has the VMStat_Exclusive bit set is assigned the execution
focus, then it will become the only VM that is allowed to run. In this case,
foreground and background priorities are meaningless since the VM is using
100 percent of the CPU time. The Release_Time_Slice service has no effect on
an exclusive virtual machine. High-priority background VMs will not run when
an exclusive VM has the execution focus.

The only exception to this is that Windows must be notified of certain
operations in a VM and will run momentarily in the background when these
operations occur. For example, if an exclusive VM is running in a window the
Windows VM will wake up occasionally to update the display.

The only exception to this is that Windows must be notified of certain
operations in a VM and will run momentarily in the background when these
operations occur. For example, if an exclusive VM is running in a window,
the Windows VM will wake up occasionally to update the display.

If the VM with the focus is not exclusive, then any VM that has the
VMStat_Background flag set will be allowed to run based on their background
time-slice priority. The VM with the focus will be scheduled based on its
foreground time-slice priority.

For this scheduler, a higher priority indicates that the VM should get more
CPU time. The larger the priority, the faster the VM will run.

The algorithm used to allocate time determines the percentage of CPU time
each VM should get based on their percentage of the total of all the
time-slice priorities. For example, assume the following VMs exist:

╓┌───┌───────────────────┌───────────────────┌───────────────────────────────╖
    Foreground          Background
VM  Priority            Priority            Flags
────────────────────────────────────────────────────────────────────────────
1   100                 50                  Exclusive, Background

2   100                 50                  Background

3   50                  25                  (None ─ Foreground,
                                            non-exclusive)

4   250                 75                  Background

────────────────────────────────────────────────────────────────────────────



If the execution focus is set to VM 1, then it will use 100 percent of the
CPU time since it has the exclusive flag set. If the execution focus is set
to VM 2, then VMs 1, 2, and 4 will run.VM 3 would not be scheduled since it
does not have the background flag set.

To determine how much time each VM should be allocated, the time-slicer
first sums all the VM priorities and, then, calculates the percentage of CPU
time each VM should receive as follows:

VM 2 foreground pri = 100 / 225 * 100 = 45% of CPU

VM 1 background pri = 50 / 225 * 100 = 22% of CPU

VM 4 background pri = 75 / 225 * 100 = 33% of CPU  ───  Total 225

Notice that a foreground priority of 10,000 (the maximum allowed) is
special. When a VM with priority 10,000 is the execution focus VM, only
high-priority background VMs will run unless the focus VM explicitly
releases its time slice. This is different from an exclusive VM since other
VMs can run if the focus gives up its time.

When a VM has the VMStat_High_Pri_Back flag set it will execute in the
background even if the current-focus VM has a priority of 10,000 or is
exclusive.

The discussion of services providing support for the Time-Slice Scheduler is
presented in the following order:


  ■   Adjust_Execution_Time

  ■   Call_When_Idle

  ■   Get_Execution_Focus

  ■   Get_Time_Slice_Granularity

  ■   Get_Time_Slice_Info

  ■   Get_Time_Slice_Priority

  ■   Release_Time_Slice

  ■   Set_Execution_Focus

  ■   Set_Time_Slice_Granularity

  ■   Set_Time_Slice_Priority

  ■   Wake_Up_VM


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


Adjust_Execution_Time
────────────────────────────────────────────────────────────────────────────


Description

This service allows a VxD to change the amount of time a VM will be allowed
to execute regardless of the VM's time-slice priority. Usually this service
is used by VxDs such as the Virtual COM Device to boost temporarily the
priority of a VM that is receiving lots of interrupts. This service can also
be used to reduce the amount of time a VM will be allowed to run by passing
a negative value in EAX. However, this is likely to cause execution
starvation and is discouraged.

The value specified in EAX is the number of additional (or fewer)
milliseconds the VM will be allowed to run. It has the same effect on all
VMs regardless of their time-slice priority. This means that if a VxD calls
this service with EAX = 1000, then the specified VM will be allowed to run
an additional second regardless of its time-slice priority.

Notice that if the specified VM is not on the time-slice execution list,
then this service will do nothing. It will not force a non-runnable VM to
execute. In other words, a non-background VM cannot be forced to run in the
background by boosting its execution time.

Be careful using this service. It can result in starvation for other
processes.


Entry

EAX = + or - milliseconds to adjust execution time by EBX = VM handle


Exit

None


Uses

Flags


Call_When_Idle
────────────────────────────────────────────────────────────────────────────


Description

This service is used by VxDs that want to perform background operations when
the system is "idle". For example, this service is used by the pageswap
device to asynchronously write dirty pages to the backing store. Windows is
considered idle when all VMs have released their time-slice. When the
Windows kernel signals that Windows is idle and all other VMs are idle, the
idle call-back procedures will be called. Each call-back can either consume
the idle call or pass it on to the next idle call-back in the list. Idle
calls should be consumed if the VxD performs an operation that takes a
significant amount of time. For example, if the pageswap device writes a
dirty page to the backing store then it will consume the idle call to
prevent sluggish performance.


Entry

ESI - Procedure to call when all VMs idle


Exit

If carry clear then  Call-back procedure installed else  Error: Could not
install call-back procedure


Uses

Flags


Call-Back

EBX = System VM Handle (current VM is always the system VM) EBP - Client
register structure Return with carry SET to pass the call to next handler
Return with carry CLEAR to "eat" the call-back and indicate Sys VM is not
idle. Call-back procedure can modify EAX, EBX, ECX, EDX, ESI, EDI, and
Flags.


Get_Execution_Focus
────────────────────────────────────────────────────────────────────────────


Description

This service returns the handle of the VM that is the focus (foreground) VM.
This service can be called from an interrupt handler.


Entry

None


Exit

EBX = Handle of VM with execution focus


Uses

EBX, Flags


Get_Time_Slice_Granularity
────────────────────────────────────────────────────────────────────────────


Description

This service returns the current time-slice granularity in EAX. The value
returned is the minimum number of milliseconds a VM will be allowed to run
before being rescheduled.


Entry

None


Exit

EAX = Minimum time-slice size in milliseconds


Uses

EAX, Flags


Get_Time_Slice_Info
────────────────────────────────────────────────────────────────────────────


Description

This service returns information about the number of virtual machines
currently scheduled by the time-slicer and the number of VMs that are idle.


This service can be called at interrupt time.


Entry

None


Exit

EAX = Number of VMs scheduled EBX = Handle of VM currently scheduled ECX =
Number of scheduled VMs that are currently idle


Uses

Nothing


Get_Time_Slice_Priority
────────────────────────────────────────────────────────────────────────────


Description

This service returns the time-slice execution flags, the foreground and
background priorities, and the percent of CPU usage for a specified VM.
Notice that the percent of CPU time returned indicates the amount of time
the VM is allowed to run, but this number will not reflect the actual amount
of CPU time if any VM releases its time slice since other VMs will be
allowed to execute during that VM's time slice.


Entry

EBX = VM handle


Exit

EAX = Flags    (Appropriate flags from CB_VM_Status control block
field)          VMMStat_Exclusive          VMStat_Background
VMStat_High_Pri_Back ECX = Foreground time-slice priority (high word 0) EDX
= Background time-slice priority (high word 0) ESI = % of total CPU time
used by VM


Uses

Flags


Release_Time_Slice
────────────────────────────────────────────────────────────────────────────


Description

This service causes the current VM to give up any time remaining in its
current time slice and allows the next VM in the time-slice queue to run.
This service should be called whenever a VM is idle to allow other VMs to
execute faster. If there is only one VM in the time-slice queue, this
service will do nothing.

When this service is called for a background VM, it will release the VM's
time-slice, and adjust the VM's execution time by -500 milliseconds. This
assures that idle background-VMs will take up very little CPU time.

If the focus VM or a high priority background VM calls this service, it will
only give up it's time slice. It's execution time will not be adjusted.


Entry

None


Exit

None


Uses

Flags


Set_Execution_Focus
────────────────────────────────────────────────────────────────────────────


Description

This service changes the time-slice exection focus to the specified virtual
machine. The VM with the focus executes with its foreground priority. If the
VMStat_Exclusive flag is set, then it will be the only VM scheduled.
Otherwise, background VMs will be allowed to run. All VMs except the focus
VM, background VMs, and the system VM will be suspended.

This service must only be called in the system VM or when the new focus is
the same as the current VM (EBX = Cur_VM_Handle).


Entry

EBX = VM handle


Exit

If carry set then  ERROR: Could not set focus else  Focus set successfully


Uses

Flags


Set_Time_Slice_Granularity
────────────────────────────────────────────────────────────────────────────


Description

This service is used to change the minimum amount of time the time-slice
scheduler will allocate to a VM. Smaller values will make multitasking
appear smoother but will increase overhead due to the large number of task
switches required. Larger values will allow more time for the VMs to execute
but may make execution appear sporadic to the user.


Entry

EAX = Minimum time-slice size in milliseconds


Exit

None


Uses

Flags


Set_Time_Slice_Priority
────────────────────────────────────────────────────────────────────────────


Description

This service sets the time-slice execution flags and the foreground and
background priorities for a specified VM.

To change part of a VM's time-slice priority status, first call
Get_Time_Slice_Priority, then change only the values you are interested in
and call this service. For example, to set a VM into background mode, you
would do the following:

  mov ebx, [Handle_Of_VM_To_Change]
  VMMcall  Get_Time_Slice_Priority
  or eax, VMStat_Background
  VMMcall  Set_Time_Slice_Priority


Entry

EAX = Flags  VMStat_Exclusive  VMStat_Background  VMStat_High_Pri_Back EBX =
VM handle ECX = Foreground priority (high word must be 0) EDX = Background
priority (high word must be 0)


Exit

If carry set then  ERROR: Could not change priority / flags for VM else
Priority and flags changed


Uses

Flags


Wake_Up_VM
────────────────────────────────────────────────────────────────────────────


Description

This service is used to "wake up" a VM that called the Release_Time_Slice
service and has the VMStat_Idle flag set. If the specified VM is not idle,
this service will do nothing.


Entry

EBX = Handle of VM begin woken up


Exit

None


Uses

Flags






Chapter 26  Event Services
────────────────────────────────────────────────────────────────────────────

Enhanced Windows is a single-threaded, non-reentrant operating environment.
Because it is non-reentrant, VxDs that hook hardware interrupts must have
some method of synchronizing their calls to VMM. For this reason, enhanced
Windows has the concept of "event" processing.

When a VxD is entered due to an asynchronous interrupt, such as a hardware
interrupt, the VxD is limited to a very specific subset of functions. It is
allowed to do only the following:


  ■   Call any Virtual PIC Device (VPICD) service

  ■   Call any asynchronous VMM service (see individual services for
      details)

  ■   Schedule events


Obviously, VxDs that service hardware interrupts will often need to use
services other than the ones listed above. When this is the case, the VxD
will need to schedule an event. When an event is scheduled, the caller
defines a procedure to call when it is OK to make any VMM call. When VMM
calls this procedure, the VxD can finish processing the asynchronous event.


VM events are often useful for VxDs that do not service hardware interrupts
and can be scheduled at any time except during a Non-Maskable Interrupt
(NMI).

When an event service routine is called, it is entered with the following:


  ■   EBX = Current VM handle

  ■   EDX = Reference data passed when the routine was set up

  ■   EBP -> Client register structure


The event callback procedure can modify EAX, EBX, ECX, EDX, ESI, and EDI.

The discussion of services providing support for events is presented in the
following order:


  ■   Call_Global_Event

  ■   Call_Priority_VM_Event

  ■   Call_VM_Event

  ■   Cancel_Global_Event

  ■   Cancel_Priority_VM_Event

  ■   Cancel_VM_Event

  ■   Hook_NMI_Event

  ■   Schedule_Global_Event

  ■   Schedule_VM_Event


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


Call_Global_Event
────────────────────────────────────────────────────────────────────────────


Description

This procedure is a faster method of servicing asynchronous events. If the
current thread of execution begins in a VM (it was not an interrupt from
within the VMM), then the event procedure will be called immediately.
Otherwise, the event will be scheduled.


Entry

ESI = Offset of procedure to call EDX = Reference data (will be passed back
to procedure)


Exit

If ESI = 0 then  Event procedure was called else  ESI = Event handle (can be
used to cancel events)


Uses

ESI, Flags


Callback

EBX = Current VM handle EDX = Reference data EBP -> Client Register
Structure


Call_Priority_VM_Event
────────────────────────────────────────────────────────────────────────────


Description

This service combines the functionality of Call_VM_Event,
Call_When_VM_Ints_Enabled, Call_When_Not_Critical, and Adjust_Exec_Priority
into one, easy to use service. As with all event services, this service can
be called from an interrupt handler.

Call_Priority_VM_Event is used by VxDs for several purposes. The most common
uses are as follows:


  ■   To wait until a VM enables interrupts and the critical section is free
      so the VxD can call DOS or some other non-reentrant code.

  ■   To boost a VM's priority and wait until the VM enables interrupts to
      simulate an interrupt type event. For example, the VNETBIOS uses this
      service for asynchronous network request POST callbacks.

  ■   To force an event to be processed in another VM by boosting the VM's
      Execution Priority.



Example

Assume a VxD implements a print spooler that will call a VM back when a
buffer has been sent to the printer. It could use this service to notify the
appropriate VM that its buffer has been printed as follows:

  VxD_Code_SEG
   BeginProc Print_Buff_Empty
   mov  eax, Low_pri_Device_boost
   mov  ebx, [Call_Back_VM_Handle]
   mov  ecx, PEF_Wait_ForSTI or PEF_Wait_Not_Crit
   mov  edx, [Call_back_CS_IP]
   mov  esi, Buff_Empty_Call_Back_Event
   VMMcall   Call_Priority_Event
   ret
   EndProc  Print_Buff_Empty
   BeginProc  Buff_Empty_Call_Back_Event
   VMMcall  Begin_Nest_Exec  ;Get ready to call VM
   mov   ecx,  edx
   shr   edx,  16   ;ECX = Segment to call
   movzx  edx, dx   ;EDX = Offset to call
   VMMcall    Build_Int_Stack_Frame
   VMMcall    Resume_Exec  ;call the VMM's callback
   VMMcall End_Nest_Exec
   ret
   EndProc    Buff_Empty_Call_Back_Event

The Print_Buff_Empty procedure could be called from a hardware interrupt
handler in any VM. It uses Call_Priority_VM_Event to force the correct VM to
be scheduled. The priority boost specified in EAX will force the event to be
processed quickly although not as fast as a hardware interrupt. The options
specified in the ECX register will force the event to be delayed until the
critical section is free and the VM's interrupts are enabled. The reference
data in EDX contains the CS:IP of the procedure to call in the VM.

When Buff_Empty_Call_Back_Event is called it can make several assumptions:
it is running in the desired VM, the critical section is not owned, and the
VM has enabled interrupts. It uses the CS:IP value passed in EDX to simulate
a pseudo-interrupt in the VM. The procedure called in the VM would have to
execute an IRET to return from the callback. When Buff_Empty_Call_Back_Event
returns, the execution priority boost is automatically deducted.

Notice this example is incomplete ─ An actual VxD handler would need to do
more work. It does not address several problems. For example
Buff_Empty_Call_Back_Event does not take into account whether the call
should be made to a V86 CS:IP or protected mode CS:IP. It also would not
work for 32-bit protected mode programs since it would need to pass a 32-bit
offset (EIP) to Simulate_Far_Call.


Entry

EAX = Priority boost (can be 0) EBX = VM handle ECX = Option flags (defined
in VMM.INC)  PEF_Wait_For_STI - Event will not be called until  VM enables
interrupts  PEF_Wait_Not_Crit - Event will not be called until  VM is not in
a critical section  or time-critical operation.  PEF_Dont_Unboost - Priority
of VM will not be reduced  after return from event procedure.
PEF_Always_Sched - Always schedule the event. This means  the event
procedure will never be called immediately.  All other bits are reserved and
must be 0. EDX = Reference data (will be passed back to procedure) ESI =
Offset of procedure to call


Exit

If ESI = 0 then  Event procedure already called  else  Event procedure will
be called later ESI = Event handle (can cancel using
Cancel_Priority_VM_Event)


Uses

Flags


Callback

EBX = Current VM handle EDX = Reference data EBP -> Client register
structure

Procedure can modify EAX, EBX, ECX, EDX, ESI, EDI, and Flags


Call_VM_Event
────────────────────────────────────────────────────────────────────────────


Description

This procedure is a faster method of servicing asynchronous events. If the
current thread of execution begins in a virtual machine (it was not an
interrupt from within the VMM) and the event is for the current VM, then the
event procedure will be called immediately. Otherwise, the event will be
scheduled.


Entry

EBX = VM handle ESI = Offset of procedure to call EDX = Reference data (will
be passed back to procedure)


Exit

If ESI = 0 then  Event procedure was called else  ESI = Event handle (can be
used to cancel events)


Uses

Flags


Callback

EBX = Current VM handle EDX = Reference data EBP -> Client register
structure


Cancel_Global_Event
────────────────────────────────────────────────────────────────────────────


Description

This service is used to cancel an event that was previously scheduled by
Schedule_Global_Event or Call_Global_Event. Notice that, once a scheduled
event is serviced, you must not attempt to cancel that event.

────────────────────────────────────────────────────────────────────────────
NOTE

It is valid to pass ESI = 0 to this service (it will do nothing). This is
provided so that code that uses this service can use 0 to indicate no event
scheduled and not have to perform a test every time it wants to cancel an
event. For example:
────────────────────────────────────────────────────────────────────────────

  xor     esi, esi
      xchg    esi, [My_Event_Handle]
      VMMcall Cancel_Global_Event

will always work even if no event was scheduled. You will also need to set
[My_Event_Handle] to 0 in your event procedure.

Entry

ESI = Event handle (0 is acceptable)


Exit

Global event has been canceled


Uses

Flags


Cancel_Priority_VM_Event
────────────────────────────────────────────────────────────────────────────


Description

This service is used to cancel an event that was previously scheduled by
Call_Priority_VM_Event. Notice that once a scheduled event is serviced, you
must not attempt to cancel that event.

────────────────────────────────────────────────────────────────────────────
NOTE

It is valid to pass ESI = 0 to this service (it will do nothing). This is
provided so that code that uses this service can use 0 to indicate no event
scheduled and not have to perform a test every time it wants to cancel an
event. For example:
────────────────────────────────────────────────────────────────────────────

  xor     esi, esi
      xchg    esi, [My_Event_Handle]
      VMMcall Cancel_VM_Event

will always work even if no event was scheduled. You will also need to set
[My_Event_Handle] to 0 in your event procedure.
Any priority boost associated with this event will be canceled even if the
PEF_DONT_UNBOOST flag was set when the event was scheduled.

Do not use this service to cancel events scheduled using the Call_VM_Event
or Schedule_VM_Event services. You must cancel normal VM events using the
Cancel_VM_Event service.


Entry

ESI = Priority event handle (0 is valid)


Exit

Event canceled, ESI contains garbage


Uses

Flags, ESI


Cancel_VM_Event
────────────────────────────────────────────────────────────────────────────


Description

This service is used to cancel an event that was previously scheduled by
Schedule_VM_Event or Call_VM_Event. Notice that, once a scheduled event is
serviced, you must not attempt to cancel that event.

────────────────────────────────────────────────────────────────────────────
NOTE

It is valid to pass ESI = 0 to this service (it will do nothing). This is
provided so that code that uses this service can use 0 to indicate no event
scheduled and not have to perform a test every time it wants  to cancel an
event. For example:
────────────────────────────────────────────────────────────────────────────

  xor     esi, esi
      xchg    esi, [My_Event_Handle]
      VMMcall Cancel_VM_Event

will always work even if no event was scheduled. You will also need to set
[My_Event_Handle] to 0 in your event procedure.
Do not use this service to cancel events scheduled using the
Call_Priority_VM_Event service. You must cancel priority events using the
Cancel_Priority_VM_Event service.


Entry

EBX = VM handle ESI = Event handle (0 is acceptable)


Exit

None


Uses

Flags


Hook_NMI_Event
────────────────────────────────────────────────────────────────────────────


Description

See documentation for Get_NMI_Handler_Addr for information on this service.
This service must only be called during initialization.


Entry

ESI = Address of NMI event procedure


Exit

None


Uses

Flags


Callback

EBX = Current VM handle EBP -> Client Register Structure


Schedule_Global_Event
────────────────────────────────────────────────────────────────────────────


Description

This procedure is used to schedule asynchronous events that are not VM
specific. The events will be processed immediately before the VMM IRETs to
any VM.


Entry

ESI = Offset of procedure to call EDX = Reference data (will be passed back
to procedure)


Exit

ESI = Event handle (can be used to cancel event)


Uses

ESI, Flags


Callback

EBX = Current VM handle EDX = Reference data EBP -> Client register
structure


Schedule_VM_Event
────────────────────────────────────────────────────────────────────────────


Description

This procedure is used to schedule asynchronous events that are VM specific.
The events will be processed immediately before the VMM IRETs to the
specified VM.

VM events will only be executed in the VM for which they were scheduled for.
Therefore, if a VM event is scheduled for a VM other than the current
virtual machine, it will not be processed until a task switch occurs to that
VM.


Entry

EBX = VM handle ESI = Offset of procedure to call EDX = Reference data (will
be passed back to procedure)


Exit

ESI = Event handle (can be used to cancel event)


Uses

ESI, Flags


Callback

EBX = Current VM handle (VM event was scheduled for) EDX = Reference data
EBP -> Client register structure






Chapter 27  Timing Services
────────────────────────────────────────────────────────────────────────────

Timing services are provided for use by VxDs that need to perform periodic
operations or need to establish the amount of time elapsed since a
particular event. They are described here in the following order:


  ■   Cancel_Time_Out

  ■   Get_Last_Updated_System_Time

  ■   Get_Last_Updated_VM_Exec_Time

  ■   Get_System_Time

  ■   Get_VM_Exec_Time

  ■   Set_Global_Time_Out

  ■   Set_VM_Time_Out

  ■   Update_System_Clock


See Chapter 16, "Overview of Windows in Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


Cancel_Time_Out
────────────────────────────────────────────────────────────────────────────


Description

This service is used to cancel a time-out that was scheduled through either
Set_VM_Time_Out or Set_Global_Time_Out.

────────────────────────────────────────────────────────────────────────────
NOTE

It is valid to pass ESI = 0 to this service (it will do nothing). This is
provided so that code that uses this service can use 0 to indicate no
time-out scheduled and not have to perform a test every time it wants  to
cancel a time-out. For example:
────────────────────────────────────────────────────────────────────────────

  xor      esi, esi
    xchg     esi, [Local_Time_Out_Handle]
    call      Cancel_Time_Out

will always work even if no time-out was scheduled.

Entry

ESI = Time-out handle to cancel OR 0 if no time-out to be canceled


Exit

Time-out is canceled, old time-out handle now invalid


Uses

Flags


Get_Last_Updated_System_Time
────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────
NOTE

The description for this service has been identified as out of date and the
updated information was unavailable for this release.
────────────────────────────────────────────────────────────────────────────


Get_Last_Updated_VM_Exec_Time
────────────────────────────────────────────────────────────────────────────

────────────────────────────────────────────────────────────────────────────
NOTE

The description for this service has been identified as out of date and the
updated information was unavailable for this release.
────────────────────────────────────────────────────────────────────────────


Get_System_Time
────────────────────────────────────────────────────────────────────────────


Description

This service will return the time in milliseconds since the enhanced Windows
environment was started. There is no way to detect rollover of the clock
through this function but the clock will take 49.5 days to roll over.

If you are concerned about rollover, you should schedule a time-out every 30
days.


Entry

None


Exit

EAX = Elapsed time in milliseconds since enhanced Windows was started


Uses

EAX, Flags


Get_VM_Exec_Time
────────────────────────────────────────────────────────────────────────────


Description

This service returns the amount of time that a particular VM has executed.
Every VM starts with an Exec_Time of 0 when it is created, and the Exec_Time
is only increased when the VM is actually executed. Therefore, the value
returned does not reflect the length of time the VM has existed. Instead, it
indicates the amount of time that task has actually been the currently
running VM.


Entry

None


Exit

EAX = Amount of time in milliseconds that VM has executed


Uses

EAX, Flags


Set_Global_Time_Out
────────────────────────────────────────────────────────────────────────────


Description

Schedules a time-out that will occur after EAX milliseconds have elapsed.

The callback procedure will be called with ECX equal to the number of
milliseconds that have elapsed since the actual time-out occurred. Time-outs
are often delayed by 10 milliseconds or more since the normal system timer
runs at 20 milliseconds or slower. If you need more accurate time-outs, then
you must increase the timer interrupt frequency. See the VTD documentation
for more details on setting the timer interrupt period.


Entry

EAX = Number of milliseconds to wait until time-out EDX = Reference data to
return to procedure ESI = Address of procedure to call when time-out occurs



Exit

If time-out was NOT scheduled then  ESI = 0 (This is useful since 0 = NO
TIME-OUT SCHEDULED)  else  ESI = Time-out handle (used to cancel time-out)


Uses

ESI, Flags


Callback

EBX = Current VM handle ECX = Number of EXTRA milliseconds that have elapsed
EDX = Reference data EBP -> Client register structure Procedure may corrupt
EAX, EBX, ECX, EDX, ESI, EDI, and Flags


Set_VM_Time_Out
────────────────────────────────────────────────────────────────────────────


Description

Schedules a time-out that will occur after a VM has executed for the
specified length of time. Notice that the time-out will occur after the VM
has run for EAX milliseconds. Therefore, if there is more that one VM
executing, it may take more than EAX milliseconds to occur.

The callback procedure will be called with ECX equal to the number of
milliseconds that have elapsed since the actual time-out occurred. Time-outs
are often delayed by 10 milliseconds or more since the normal system timer
runs at 20 milliseconds or slower. If you need more accurate time-outs, then
you must increase the timer interrupt frequency. See the VTD documentation
for more details on setting the timer interrupt period.


Entry

EAX = Number of milliseconds to wait until time-out EBX = VM handle EDX =
Reference data to return to procedure ESI = Address of procedure to call
when time-out occurs


Exit

  If time-out was NOT scheduled then
      ESI = 0 (This is useful since 0 = NO TIME-OUT SCHEDULED)
  else
      ESI = Time-out handle (used to cancel time-out)


Uses

ESI, Flags


Callback

EBX = Current VM handle (VM time-out was scheduled for) ECX = Number of
EXTRA milliseconds that have elapsed EDX = Reference data EBP -> Client
register structure Procedure may corrupt EAX, EBX, ECX, EDX, ESI, EDI, and
Flags.


Update_System_Clock
────────────────────────────────────────────────────────────────────────────


Description

This service must be called only by the Virtual Timer Device. If more than
one device calls this service, then the VMM timing services will not behave
correctly. The timer calls this procedure to update the current system time
and the current VM's execution time. The value passed in ECX is the number
of milliseconds that have elapsed since the last call to this service. In
other words, if the current system time is n, then, after a call to
Update_System_Clock, the current system time would be n+ECX.

This service assumes interrupts are disabled!


Entry

ECX = Elapsed time in milliseconds


Uses

Flags






Chapter 28  Processor Fault and Interrupt Services
────────────────────────────────────────────────────────────────────────────

The discussion of services providing general support for processor faults
and interrupts are presented in the following order:


  ■   Get_Fault_Hook_Addrs

  ■   Get_NMI_Handler_Addr


@lb1 = Hook_PM_Fault


  ■   Hook_NMI_Event


@lb1 = Hook_V86_Fault


  ■   Hook_V86_Page


@lb1 = Hook_VMM_Fault


  ■   Set_NMI_Handler_Addr


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


Get_Fault_Hook_Addrs
────────────────────────────────────────────────────────────────────────────


Description

Returns the address of the V86 mode, PM application, and VMM reenter fault
handlers for a specified fault. If the fault does not have a handler, then
this procedure will return 0. You cannot get the hook address for interrupt
2 (NMI). You must use the Get/Set_NMI_Handler_Addr services to hook
interrupt 2.


Entry

EAX = Interrupt number


Exit

If carry clear then  EDX = Address of V86 Mode App fault handler (0 if none
installed)  ESI = Address of Prot Mode App fault handler (0 if none
installed)  EDI = Address of VMM Re-enter fault handler (0 if none
installed)  else  ERROR: Invalid fault number


Uses

Flags


Get_NMI_Handler_Addr
────────────────────────────────────────────────────────────────────────────


Description

If a VxD needs to hook the Non-Maskable Interrupt (NMI), it must first call
this service to get the current NMI handler address, save the address so the
current handler can be chained to it, and then set the new address.

Notice that your NMI interrupt handler can only touch local data in the
device's VxD_LOCKED_DATA_SEG. It cannot touch memory in a VM handle, V86
memory, or any other memory. It also cannot call any services, including
services that can be called during normal hardware interrupts. Because an
NMI can occur at any time, it is difficult to do much of anything during
interrupt time that is guaranteed not to reenter a non-reentrant procedure
or affect a data structure.

Most NMI handlers will want to have an NMI event handler. This handler is
similar to a normal event handler except that you only need to hook the NMI
event chain once instead of scheduling an event every time. Every NMI event
handler will be called every time an NMI occurs. Thus, most NMI interrupt
routines simply detect that the NMI is for them and set a variable that
their NMI event handler uses to perform some function. For example:

  Initialization:
      VMMcall Get_NMI_Handler_Addr
      mov     [NMI_Chain_Addr], esi
      mov     esi, OFFSET32 My_NMI_Handler
      VMMcall Set_NMI_Handler_Addr
      mov     esi, OFFSET32 My_NMI_Event
      VMMcall Hook_NMI_Event
      clc
      ret

  My_NMI_Handler:
      in    al, My_Stat_Port
      test  al, My_Int_Mask
      jz    SHORT MNH_Exit
      inc   [NMI_From_Me]
  MNH_Chain:
      jmp   [NMI_Chain_Addr]

  My_NMI_Event:
      xor   al, al
      xchg  al, [NMI_From_Me]
      test  al, al
      jz    SHORT NME_Exit
   (Do something here ─ NMI from my device)
  MNE_Exit:
      ret


Entry

None


Exit

ESI = Offset of current NMI handler


Uses

ESI, Flags


Hook_NMI_Event
────────────────────────────────────────────────────────────────────────────


Description

See the documentation mentioned earlier in this chapter on
Get_NMI_Handler_Addr for information on this service.


Entry

ESI = Address of NMI event procedure


Exit

None


Uses

Flags


Callback

EBX = Current VM handle EBP -> Client register structure

Procedure may corrupt EAX, EBX, ECX, EDX


Hook_V86_Fault, Hook_PM_Fault, Hook_VMM_Fault
────────────────────────────────────────────────────────────────────────────


Description

These services replace the fault handler procedure address with the
procedure supplied. They will return the old fault handler's address or 0 to
indicate that there was no previous fault handler. If the value returned in
ESI is non-zero, then you may chain to the next handler with ALL REGISTERS
PRESERVED. Your handler can "eat" a fault without chaining by executing a
near return (not an IRET) and can modify EAX, EBX, ECX, EDX, ESI, and EDI.

If you hook a fault during the Sys_Critical_Init phase of device
initialization, your fault handler will be "behind" any VMM fault handler.
If the VMM cannot properly handle a fault (for example, a General Protection
fault), then it will chain to the next handler. By hooking GP faults during
Sys_Critical_Init your VxD can intercept any GP fault that would otherwise
crash the current VM. Any hooks installed after Sys_Critical_Init will be
placed "in front of" the default VMM fault handlers. This allows devices to
examine faults before they are processed by the VMM.

Note that the processor Non-Maskable Interrupt (NMI) must be hooked using
the Get/Set_NMI_Addr services (do not call Hook_xxx_Fault with EAX = 2).
Also, hardware interrupts should be hooked using the Virtual Programmable
Interrupt Controller Device (VPICD). A VxD should NOT attempt to circumvent
the VPICD using these services.

For version 3.0 of enhanced Windows, the largest interrupt number available
is 4FH. Interrupts 00H-1FH are reserved by Intel for processor faults.
Interrupts 20H-2FH are reserved by enhanced Windows. Interrupts 50H-5FH are
used by the VPICD. Interrupts 40H and 41H are used by the debugger.
Interrupts 42H-4FH are free for use by VxDs.


Entry

EAX = Interrupt number ESI = Procedure offset


Exit

If carry clear then  ESI = Old procedure offset (0 if none) else  ERROR:
Invalid fault number in EAX


Uses

ESI, Flags


Callback

Interrupts disabled EBX = Current VM handle If fault from V86 or PM app then
EBP -> Client_Register_Strucuture else  VMM reentered ─ Only asynchronous
services may be called.  EBP -> VMM re-entrant fault stack frame

If your handler chains, then it must preserve all registers (even registers
not documented as entry conditions to this callback).


Hook_V86_Page
────────────────────────────────────────────────────────────────────────────


Description

This service allows VxDs to intercept page faults in portions of the V86
address space of every virtual machine. It is used by devices such as the
Virtual Display Device to detect when particular address ranges are
accessed.

You must specify a page number and address of a callback routine to this
service. If it is installed successfully, your hook will be called every
time a page fault occurs in any VM on that page. See the memory manager
_Modify_Pages documentation in Chapter 19, "Memory Management Services," for
making hooked pages not present and for registering the ownership of pages.


The callback routine is responsible for mapping memory at the location of
the page fault or crashing the VM. In unusual circumstances, it may be
appropriate to map a NULL page at the faulting address page. See the memory
manager documentation for details on mapping memory and mapping NULL pages.


────────────────────────────────────────────────────────────────────────────
NOTE

Do not rely on the contents of the CR2 (page fault) register. Use the value
passed to your callback in EAX.
────────────────────────────────────────────────────────────────────────────


Entry

EAX = Page number (A0h - FFh) ESI = Address of trap routine


Exit

If carry flag set then  ERROR: Invalid page number or page already hooked
else  Page hooked


Uses

Procedure may corrupt EAX, EBX, ECX, EDX, ESI, EDI, and Flags


Callback

EAX = Faulting page number EBX = Current VM handle EBP does NOT point to the
client register structure.


Set_NMI_Handler_Addr
────────────────────────────────────────────────────────────────────────────


Description

See the documentation mentioned earlier in this chapter on
Get_NMI_Handler_Addr for information on this service.


Entry

ESI = Offset of new NMI handler


Exit

None


Uses

Flags






Chapter 29  Information Services
────────────────────────────────────────────────────────────────────────────

These services return the requested information without instigating any
other action.

They provide information on the following:


  ■   VM handles

  ■   The VMM reenter count

  ■   HMA XMS

  ■   Installation status of the debugger


They are described here in the following order:


  ■   Get_Cur_VM_Handle

  ■   Get_Next_VM_Handle

  ■   Get_Sys_VM_Handle

  ■   Get_VMM_Reenter_Count

  ■   Get_VMM_Version

  ■   GetSet_HMA_Info

  ■   Test_Cur_VM_Handle

  ■   Test_Debug_Installed

  ■   Test_Sys_VM_Handle

  ■   Validate_VM_Handle


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


Get_Cur_VM_Handle
────────────────────────────────────────────────────────────────────────────


Description

This service returns the handle to the currently running VM. It is valid to
call this service at interrupt time.


Entry

None


Exit

EBX = Current VM handle


Uses

EBX, Flags


Get_Next_VM_Handle
────────────────────────────────────────────────────────────────────────────


Description

VMM maintains a list of all valid VM handles. This service provides a means
of scanning the list easily. Normally, code that uses this service looks
something like this:

  VMMcall Get_Cur_VM_Handle
   Scan_Loop:
   ...
   (Do something to VM state)
   ...
   VMMcall Get_Next_VM_Handle
   VMMcall  Test_Cur_VM_Handle
   jne Scan_Loop

This allows the state of every VM to be modified. However, there are also
other uses for this service. There is no guaranteed ordering of the list
other than the fact that each VM will appear in the list only once. Notice
also that the list is circular so you will need to test for the end case
(Next VM = First VM). It is valid to call this service at interrupt time.


Entry

EBX = VM handle


Exit

EBX = Next VM handle in VM list


Uses

EBX, Flags


Get_Sys_VM_Handle
────────────────────────────────────────────────────────────────────────────


Description

This service returns the System VM handle. It is valid to call this service
at interrupt time.


Entry

None


Exit

EBX = System VM handle


Uses

EBX, Flags


Get_VMM_Reenter_Count
────────────────────────────────────────────────────────────────────────────


Description

This service is used to determine if the VMM has been reentered from an
interrupt. The normal situation for reentering VMM is from a hardware
interrupt, page fault, or other processor exception. Since most VMM services
are non-reentrant, this test should be used to determine if other VMM
services can be called or if a global event should be scheduled. Notice that
the Call_Global_Event service tests this condition automatically and will
schedule an event if VMM has been reentered.


Entry

None


Exit

ECX = 0 indicates VMM has NOT been re-entered. If  0 then ECX = # of times
re-entered


Uses

Flags


Get_VMM_Version
────────────────────────────────────────────────────────────────────────────


Description

This service returns the Windows VMM version.


Entry

None


Exit

AH = Major version number (3) AL = Minor version number (0) Carry flag clear



Uses

EAX , Flags


GetSet_HMA_Info
────────────────────────────────────────────────────────────────────────────


Description

This service returns and sets information related to the HMA XMS region.

This service is intended to assist the XMS driver that is part of the
V86MMGR device. It allows the protected-mode XMS code to find out if there
was a global HMA user in before enhanced Windows was started and allows
access to the Enable count variable (Get and Set). This service is always
valid (i.e., not restricted to initialization).


Entry

ECX == 0 Get ECX  0 Set  DX = A20 enable count to set for enhanced Windows
loader  NOTE THAT THE GLOBAL HMA FLAG CANNOT BE SET. It is not  appropriate
or valid to set this.


Exit

If Get   EAX == 0 if enhanced Windows DID NOT allocate the HMA (GLOBAL HMA
User)   EAX  0 if enhanced Windows allocated the HMA (NO GLOBAL HMA User)
EDX = A20 enable count before enhanced Windows came in If Set


Uses

EAX, EDX, Flags


Test_Cur_VM_Handle
────────────────────────────────────────────────────────────────────────────


Description

This routine tests to see if the given VM handle is the handle of the
currently running VM. It is valid to call this service at interrupt time.


Entry

EBX = VM handle to test


Exit

Zero flag is set if VM handle passed in is currently running VM's handle.


Uses

Flags


Test_Debug_Installed
────────────────────────────────────────────────────────────────────────────


Description

Tests internal flag that indicates whether a debugger exists or not. It is
valid to call this service at interrupt time.


Entry

None


Exit

Zero flag = Debugger NOT installed (i.e., jz No_Debug_Installed)


Uses

Flags


Test_Sys_VM_Handle
────────────────────────────────────────────────────────────────────────────


Description

This routine tests to see if the given VM handle is the handle of the system
VM. It is valid to call this service at interrupt time.


Entry

EBX = VM handle to test


Exit

Zero flag is set if VM handle passed in is system VM's handle. (je
Is_Sys_VM)


Uses

Flags


Validate_VM_Handle
────────────────────────────────────────────────────────────────────────────


Description

This service is used to test the validity of a VM handle. This service can
be called at interrupt time.


Entry

EBX = VM handle to test


Exit

If carry flag set then    ERROR:VM handle is invalid else    Value in EBX is
a valid VM handle


Uses

Flags






Chapter 30  Initialization Information Services
────────────────────────────────────────────────────────────────────────────

These services provide access to the SYSTEM.INI file and the environment
variables. Configurable VxDs will use these services to get their
configuration parameters. They are described here in the following order:


  ■   Convert_Boolean_String

  ■   Convert_Decimal_String

  ■   Convert_Fixed_Point_String

  ■   Convert_Hex_String

  ■   Get_Config_Directory

  ■   GetDOSVectors

  ■   Get_Environment_String

  ■   Get_Exec_Path

  ■   Get_Machine_Info

  ■   Get_Next_Profile_String

  ■   Get_Profile_Boolean

  ■   Get_Profile_Decimal_Int

  ■   Get_Profile_Fixed_Point

  ■   Get_Profile_Hex_Int

  ■   Get_Profile_String

  ■   Get_PSP_Segment

  ■   OpenFile


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


Convert_Boolean_String (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service attempts to determine if the string pointed to by EDX is TRUE
or FALSE. There are many valid values for TRUE and FALSE. A short list of
valid values for TRUE are:

 True, Yes, On, 1

For false they include:

 False, No, Off, 0

This list may grow to include other words such as "oui" and "ja." This
service is only valid during initialization.


Entry

EDX = Pointer to ASCIIZ string to convert to boolean


Exit

If carry clear then  EAX = 0 if FALSE, -1 if TRUE, zero flag NOT set else
String was not a valid boolean (EAX not changed)


Uses

Flags, EAX


Convert_Decimal_String (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service converts a string that contains a decimal value and returns the
value in EAX. It also returns a pointer to the character that terminated the
decimal integer value. This is useful for parsing entries such as:

 FOO=100,300

since the 100 would be returned with EDX pointing to the ",". The pointer
could be incremented one byte and, then, this service called again to
evaluate the second number.

Notice that a NULL string or a string that does not contain a valid decimal
integer will return 0 and EDX will not be advanced since the first character
of the string terminated the analysis. This service is only valid during
initialization.


Entry

EDX = Pointer to ASCIIZ string to convert to integer


Exit

EAX = Value of decimal string EDX = Pointer to terminating character
(non-valid decimal char)


Uses

EAX, EDX, Flags


Convert_Fixed_Point_String (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service returns the value of a fixed point decimal number string
pointed to by EDX. Use Get_Profile_String to initialize EDX to point to the
string to be parsed. Fixed Point is zero or more decimal digits followed by
a terminator or a decimal point followed by zero or more decimal digits. The
value returned is ECX*10*>. Note that decimal digits beyond the accuracy
specified by ECX are ignored in the value returned in EAX, but EDX points to
the byte following the last valid ASCII decimal digit. Values that begin
with a minus will evaluate to negative numbers. Positive values may
optionally begin with a plus sign.

This service is only valid during initialization.


Entry

ECX = Number of decimal places EDX = Pointer to ASCIIZ string to convert to
integer


Exit

EAX = Value of fixed point string EDX = Pointer to terminating character
(non-valid character)


Uses

EAX, EDX, Flags


Convert_Hex_String (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service converts the string pointed to by EDX to Hexadecimal.
Hexadecimal is zero or more hexadecimal digits (0-9, A-F) followed by a
terminating character or a small or capital letter "h". The "h" has no
effect on the value. EDX is left pointing to the next byte after the "h" or,
if the "h" is not present, after the last valid hexadecimal digit. Use
Get_Profile_String to set up EDX to point to the string to be parsed. This
service is only valid during initialization.


Entry

EDX -> ASCIIZ string to convert to integer


Exit

EAX = Value of hexadecimal string EDX advanced to terminating character
(non-valid hex char)


Uses

Flags


Get_Config_Directory (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service returns a pointer to the directory that contains the
configuration files for the enhanced Windows environment (such as
SYSTEM.INI). The string returned is guaranteed to be a valid, fully
qualified pathname that ends with a terminating "\" followed by a NULL (0)
byte.


Entry

None


Exit

EDX = Pointer to ASCIIZ directory name


Uses

EDX, Flags


Get_Environment_String (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service takes a pointer to an ASCIIZ string that is the name of an
environment variable and returns a pointer to an ASCIIZ string that is the
value of that environment variable. Environment variables are set using the
MS-DOS SET command and should be of the format "SET <variable
name>=<variable value>" with no intervening spaces between the variable
name, the equal sign, and the variable value. Environment strings are an
alternative way of setting parameters for virtual device drivers. In
general, these should be used sparingly, as the environment is of limited
size. Use environment strings only when the value is a global entity, used
by more than one program or device driver. This service is only valid during
initialization.


Entry

ESI = pointer to ASCIIZ string environment variable name


Exit

If carry is set then  Environment string was not found else  EDX = pointer
to ASCIIZ string value of environment variable


Uses

EDX, Flags


Get_Exec_Path (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service returns a pointer to an ASCIIZ string that gives the full path
by which WIN386.EXE was executed. It is used to locate files associated with
the enhanced Windows environment or the virtual device drivers that are not
in subdirectories indicated by the PATH environment variable. This service
is only valid during initialization.


Entry

None


Exit

EDX = Pointer to ASCIIZ string of full path name + program name (program
name is  "WIN386.EXE") ECX = Number of characters in string up to and
including the last "\"


GetDOSVectors (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service returns the "true" INT 23 and INT 24 MS-DOS vectors. The
enhanced Windows loader points these vectors at its own handlers which have
the correct behavior for the enhanced Windows LOAD. When a VM is started up
we wish to reset these vectors to the handlers that were in place before the
loader changed them.

THIS SERVICE SHOULD ONLY BE USED BY THE DOSMGR DEVICE.


Entry


Exit

EAX = Segment:Offset (V86 address) that INT 23 pointed to before loader EDX
= Segment:Offset (V86 address) that INT 24 pointed to before loader


Uses


Get_Machine_Info (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service returns information about the computer system running enhanced
Windows.


Entry

None


Exit

AH = MS-DOS Major Version AL   = MS-DOS Minor Version BH   = MS-DOS OEM
serial number BL   = Machine Model Byte (at F000:FFFE in system ROM) HIGH 16
bits of EBX are other flags  GMIF_80486 EQU 10000h 80486 processor
GMIF_PCXT EQU 20000h PC/XT(tm) accelerator  GMIF_MCA EQU 40000h Micro
Channel(tm)  GMIF_EISA EQU 80000h EISA EDX = Equipment flags (as returned
from Int 11h) ECX = 0 if not PS/2 or extended BIOS, else ECX contains a
ring 0 linear address to System Configuration Parameters  returned from BIOS
service Int 15h, AH=C0h. See the PS/2  BIOS documentation for details on
this structure.


Uses

EAX, EBX, ECX, EDX, Flags


Get_Next_Profile_String (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service, given a pointer to a profile string, will return a pointer to
the next profile string with the key name provided. It is used by devices
that have multiple entries with the same key name. First, use
Get_Profile_String to get the first entry with a given key name and, then,
use this service to get subsequent entries. Do not modify the string
returned. This service is only valid during initialization.


Entry

EDX = Pointer returned from previous Get_(Next)_Profile_String  EDI   =
Pointer to key name string


Exit

If carry clear then  EDX = NEXT string from SYSTEM.INI else  No more
matching entries found


Uses

EDX, Flags


Get_Profile_Boolean (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service returns the value of a Boolean profile entry from the
SYSTEM.INI file in EAX. If the profile string is not found, then EAX will
not be modified. Profile entries are of the form:

 [SectionName]   KeyName=>

That is, Section Name is delineated by square brackets and KeyName is
followed by an equal sign. Neither name should have any spaces or
nonprintable characters. The value following the equal sign can be in a
number of formats. Boolean is "Yes," "No," "Y," "N," "True," "False," "On,"
"Off," "1," or "0"(foreign versions of Windows may add other language
equivalents to the above). Logical TRUE returns -1 and logical FALSE returns
0.

This service is only valid during initialization.


Entry

EAX = Default value ESI = Pointer to section name string or 0 for [386enh]
EDI = Pointer to key name string


Exit

If carry set  Entry not found or invalid boolean value  EAX = Default value
else  If value string was null,  zero flag is set and  EAX = Default value
else  EAX = 0 if FALSE, -1 if TRUE SYSTEM.INI entry value


Uses

Flags


Get_Profile_Decimal_Int (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service returns the value of a decimal profile entry from the
SYSTEM.INI file in EAX. If the profile string is not found, then EAX will
not be modified. Profile entries are of the form:

 [SectionName]  KeyName=>

That is, SectionName is delineated by square brackets and KeyName is
followed by an equal sign. Neither name should have any spaces or
non-printable characters. The value following the equal sign must be a
decimal value. It can begin optionally with a plus (+) or minus (-) and must
contain all decimal digits with no embedded spaces or decimal points.

This service is only valid during initialization.


Entry

EAX = Default value (optional) ESI = Pointer to section name string or 0 for
[386enh] EDI = Pointer to key name string


Exit

If carry is set  Entry was NOT found  EAX = Default value (value passed to
this procedure) else  If value string was null, zero flag is set and  EAX =
Default value  else  EAX = Value of SYSTEM.INI entry


Uses

Flags


Get_Profile_Fixed_Point (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service returns the value of a fixed point decimal number profile entry
from the SYSTEM.INI file in EAX. If the profile string is not found, then
EAX will not be modified. Profile entries are of the form:

 [SectionName]  KeyName=>

That is, SectionName is delineated by square brackets and KeyName is
followed by an equal sign. Neither name should have any spaces or
nonprintable characters. The value following the equal sign can be in a
number of formats. Fixed Point values may begin with an optional plus (+) or
minus (-) followed by zero or more decimal digits followed by a terminating
character or by a decimal point followed by zero or more decimal digits. The
value returned is 10^ECX*>.

This service is only valid during initialization.


Entry

EAX = Default value ECX = Number of decimal places ESI = Pointer to section
name string or 0 for [386enh] EDI = Pointer to key name string


Exit

If carry is set  Entry was NOT found  EAX = Default value (value passed to
this procedure) else  If value string was null, zero flag is set and  EAX =
Default value else  EAX = Value of SYSTEM.INI entry


Get_Profile_Hex_Int (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service returns the value of a hexadecimal number profile entry from
the SYSTEM.INI file in EAX. If the profile string is not found, then EAX
will not be modified. Profile entries are of the form:

 [SectionName]  KeyName=>

That is, SectionName is delineated by square brackets and KeyName is
followed by an equal sign. Neither name should have any spaces or
nonprintable characters. The value following the equal sign can be in a
number of formats. Hexadecimal is zero or more hexadecimal digits (0-9, A-F)
followed by a terminating character or a small or capital letter "h." The
"h" has no effect on the value. If the value following the equal sign is not
a valid hexadecimal number, EAX is unchanged.

This service is only valid during initialization.


Entry

EAX = Default value (optional) ESI = Pointer to section name string or 0 for
[386enh] EDI = Pointer to key name string


Exit

If carry is set  Entry was NOT found  EAX = Default value (value passed to
this procedure) else  If value string was null  zero flag is set  EAX =
Default value  else  EAX = Value of SYSTEM.INI entry


Uses

Flags


Get_Profile_String (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service searches the initialization file for a specified entry and
returns a pointer to a string. Do not modify the string in place. The
pointer returned points into the initialization file data area. If you need
to modify the string, you must first copy it and, then, modify it. This
service is only valid during initialization.


Entry

EDX = Pointer to default string (optional) ESI = Pointer to program name
string or 0 for [386enh] EDI = Pointer to key name string


Exit

If carry clear  EDX = Pointer to ASCIIZ string from SYSTEM.INI else  EDX is
unchanged


Uses

Flags, may change EDX


Get_PSP_Segment (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

This service returns the segment of the WIN386.EXE PSP. Use it to locate PSP
values other than the EXEC path and environment variables since separate
services are available for retrieving those ASCIIZ strings. Notice that a
segment value is returned. To convert the segment to an address, shift the
value left by 4 bits. This service is only valid during initialization.


Entry

None


Exit

EAX = Segment of WIN386.EXE PSP (high word always = 0)


Uses

EAX, Flags


OpenFile (Initialization only)
────────────────────────────────────────────────────────────────────────────


Description

Open a file via searching in the standard Windows Places:

WINDIR= ARGV[0] Current Working Directory Path


Entry

EDX -> Name to open. If this string contains a drive letter or  path seps
this routine does not search. Else the above search  strategy is employed.
EDI -> Buffer to hold name of file opened (at least 128 bytes)  Notice that
this buffer should contain a correctly formed path  for the file but this is
not guarenteed. This aspect of the behavior is dependent on the WINDIR and
PATH environment  variables being well formed.

Must be able to do Exec_Int activity in the current VM. Temp_V86_Data_Area
must not be allocated.


Exit

Carry Set  File could not be opened (file not found) Carry Clear  EAX = DOS
File handle (low 16 bits) OPEN IN MODE 0 (for read only)


Uses

EAX, FLAGS






Chapter 31  Linked List Services
────────────────────────────────────────────────────────────────────────────

These services provide a convenient set of routines for managing a
linked-list data structure. They are described here in the following order:



  ■   List_Allocate

  ■   List_Attach

  ■   List_Attach_Tail

  ■   List_Create

  ■   List_Deallocate

  ■   List_Destroy

  ■   List_Get_First

  ■   List_Get_Next

  ■   List_Insert

  ■   List_Remove

  ■   List_Remove_First


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


List_Allocate
────────────────────────────────────────────────────────────────────────────


Description

This service allocates a new node for the list specified by ESI. The
contents of the node are undefined (probably nonzero). Normally, a node is
immediately attached to the list through the List_Attach or List_Insert
services after it has been allocated.


Entry

ESI = List handle


Exit

If list was created with LF_Alloc_Error flag then  If carry clear then  EAX
-> New node  else  Error:Could not allocate node else  EAX -> New node
(Current VM crashed if node can not be allocated ─ Service  never returns to
caller)


Uses

EAX, Flags


List_Attach
────────────────────────────────────────────────────────────────────────────


Description

This service attaches a list node to the head (i.e., front) of a list.
Notice that EAX must point to a node that was allocated using List_Allocate.


Nodes can be attached to any list that has the same size node. This can be
used, for example, to move a node from one list to another.

(This figure may be found in the printed book).


Entry

ESI = List handle EAX -> Node


Exit

Node attached to list


Uses

Flags


List_Attach_Tail
────────────────────────────────────────────────────────────────────────────


Description

This service attaches a list node to the tail (i.e., end) of a list. EAX
must point to a node that was allocated using List_Allocate.

Nodes can be attached to any list that has the same size node. This can be
used, for example, to move a node from one list to another.

(This figure may be found in the printed book).


Entry

ESI = List handle EAX -> Node to insert


Exit

Node inserted at tail (end) of list


Uses

Flags


List_Create
────────────────────────────────────────────────────────────────────────────


Description

This service is used to create a new list structure. This service returns a
list handle that is used when calling all subsequent list services.

Lists normally allocate nodes from a "pool" of free nodes. This prevents the
overhead that would be incurred by calling _HeapAlloc and _HeapFree for
every list allocation and deallocation. Once a node is created, it is never
destroyed. Instead, List_Deallocate places the node back in the free pool.
The node can then be reclaimed quickly when List_Allocate is called.

If the size of the list nodes are large, you should force them to be
allocated from the system heap by setting the LF_Use_Heap flag. All
allocate/deallocate calls for lists created in this way will use _HeapAlloc
and _HeapFree to create and destroy nodes.

If you want to be able to access a list during hardware interrupts, you
should set the LF_Async flag. This forces list operations to be atomic
operations (they cannot be re-entered). If you select this option, you must
call list services with INTERRUPTS DISABLED or an error will occur. You must
disable interrupts even if you are not calling the list service from an
interrupt. Remember, always use pushf/CLI/popf to disable interrupts. Never
explicitly use STI unless other documentation states that this is
permissable. Notice that since _HeapAllocate and _HeapFree cannot be called
from a hardware interrupt, you cannot select this option and LF_Use_Heap.

The LF_Alloc_Error flag should be used if you would like to recover from an
allocation error (i.e., out of memory). The default behavior for a failed
allocation is to crash the current VM. However, if your VxD would like to
have the allocation return an error, set this flag. If this option is
selected, then List_Allocate will return with the Carry flag set when an
allocation fails. Otherwise, it will crash the current virtual machine
whenever it cannot allocate a new node.


Entry

EAX = Flags  LF_Use_Heap - All data on system heap (Can't use with LF_Async)
LF_Async - List services can be called at interrupt time  LF_Alloc_Error -
Return from alloc with carry set if can't allocate  ECX = Node size


Exit

If Carry Flag is clear then  ESI = List handle else  Error: Unable to create
list


Uses

ESI, Flags


List_Deallocate
────────────────────────────────────────────────────────────────────────────


Description

This service places a list node in the free memory pool. Once a node has
been deallocated, it should not be referenced again. You must remove the
node from any list to which it is attached before deallocating it.


Entry

ESI = List handle EAX -> List node


Exit

EAX is undefined


Uses

EAX, Flags


List_Destroy
────────────────────────────────────────────────────────────────────────────


Description

This service deallocates all nodes on a list and destroys the list handle.
Once a list has been destroyed, its handle is no longer valid.


Entry

ESI = List handle


Exit

ESI is undefined List is destroyed, all nodes deallocated.


Uses

ESI, Flags


List_Get_First
────────────────────────────────────────────────────────────────────────────


Description

This service returns a pointer to the first node in a list. If the list is
empty, it will return 0 and the Zero Flag will be set.


Entry

ESI = List handle


Exit

  If ZF is clear then
      EAX -> First node in list
  else
      List is empty. EAX = 0.


Uses

EAX, Flags


List_Get_Next
────────────────────────────────────────────────────────────────────────────


Description

This service returns the next node in a list. It is used to traverse the
list when searching for a specific element. If the end of the list is
reached, it will return 0 and the Zero Flag will be set.

Typically, this service is used in conjunction with List_Get_First to scan
an entire list.

  EXAMPLE:
    BeginProc Scan_My_List
    mov  esi, [My_List_Handle]
    VMMcall List_Get_First
    jz  SHORT Scan_Done
    Scan_Loop:
    (Do something with EAX here)
    VMMcall List_Get_Next
    jnz  Scan_Loop
    Scan_Done:
    ret
    EndProc Scan_My_List


Entry

ESI = List handle EAX -> Node


Exit

If ZF is clear then  EAX -> Next node in list else  End of list reached. EAX
= 0.


Uses

EAX, Flags


List_Insert
────────────────────────────────────────────────────────────────────────────


Description

This service inserts a node at a specified point in a list. The caller must
specify two nodes: the node to be inserted in EAX, and a position to insert
the node after in ECX. This means that node EAX will occupy the position in
the list immediately after node ECX. If ECX is zero, then node EAX will be
inserted at the head of the list.

Nodes can be inserted in any list that has the same size node. This can be
used, for example, to move a node from one list to another.

(This figure may be found in the printed book).


Entry

ESI = List handle EAX -> Node to insert ECX -> Node to insert after (0 to
attach to head)


Exit

Node inserted in list


Uses

Flags


List_Remove
────────────────────────────────────────────────────────────────────────────


Description

This service removes a specified node from a list.The node will not be
deallocated by this service. It is up to the caller to deallocate the node
or attach it to another list (it can only be attached to a list with node
size equal to the original list).

(This figure may be found in the printed book).


Entry

ESI = List handle EAX -> Node to remove from list


Exit

Node removed from list


Uses

Flags


List_Remove_First
────────────────────────────────────────────────────────────────────────────


Description

This service removes the first node from a list. Notice that the node is not
deallocated by this service. It is up to the caller to deallocate the node
or attach it to another list (it can only be attached to a list with node
size equal to the original list).

(This figure may be found in the printed book).


Entry

ESI = List handle


Exit

If Zero Flag is clear then  EAX -> Node that has been removed from list else
List is empty and EAX = 0


Uses

EAX, Flags






Chapter 32  Error Condition Services
────────────────────────────────────────────────────────────────────────────

These error services are used by VxDs when they have detected the VM to be
in an unrecoverable state. Examples of situations that might lead to such a
state include an attempted VM execution of a protected instruction or an
operation which might fail due to lack of memory. The services are described
here in the following order:


  ■   Crash_Cur_VM

  ■   Fatal_Error_Handler

  ■   Fatal_Memory_Error


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


Crash_Cur_VM
────────────────────────────────────────────────────────────────────────────


Description

This service will crash the current VM. It is to be called when a
catastrophic error has occured in the VM, such as executing an illegal
instruction or attempting to program a piece of hardware in a way
incompatible with the device virtualization.

If the system VM is the current VM, enhanced Windows will exit with a fatal
error without explicitly crashing the other VMs.


Entry

None


Exit

None


Fatal_Error_Handler
────────────────────────────────────────────────────────────────────────────


Description

This service is called (or jumped to) when a fatal error is detected. It
returns to real mode and, optionally, prints out an error message. You can
hang the computer by selecting the "EF_Hang_On_Exit" flag (defined in
VMM.INC). All of the devices that have been initialized are informed about
the exit before returning to real mode.

The "Fatal_Error" macro supplied in VMM.INC is a convienient way of calling
this service. Examples:

  Fatal_Error                     ;
  This exits with no error message
  Fatal_Error OFFSET32 My_Err_Msg ;
  Exits and prints error message

────────────────────────────────────────────────────────────────────────────
NOTE

Since this routine can be called by _InitializeMemoryManager, no assumptions
about the contents of any registers or data structures should be made.
────────────────────────────────────────────────────────────────────────────


Entry

ESI = Ptr ASCIIZ string to display (0 if none) EAX = Exit flags to send to
the loader (real mode exit code)  Bit 0 = 1 - Hang system on exit to real
mode  Others undefined and must be 0


Exit

Does not return


Uses

All registers


Fatal_Memory_Error
────────────────────────────────────────────────────────────────────────────


Description

This routine calls the Fatal_Error_Handler with exit flags equal to zero and
the message "Insufficient Memory to Initialize Windows in 386 enhanced
mode." It should be called during intialization if there is not enough
memory to initialize.

────────────────────────────────────────────────────────────────────────────
NOTE

Since this routine can be called by _InitializeMemoryManager, no assumptions
about the contents of any registers or data structures should be made.
────────────────────────────────────────────────────────────────────────────


Entry

None


Exit

None


Uses

All registers






Chapter 33  Miscellaneous Services
────────────────────────────────────────────────────────────────────────────

The services discussed in this chapter provide functions not easily
categorized such as hooking another VxDs API and sending system control
messages. They are provided here in the following order:


  ■   Begin_Reentrant_Execution

  ■   End_Reentrant_Execution

  ■   Hook_Device_Service

  ■   Hook_Device_V86_API

  ■   Hook_PM_Device_API

  ■   Map_Flat

  ■   Map_Lin_To_VM_Addr

  ■   MMGR_SetNULPageAddr

  ■   Set_System_Exit_Code

  ■   Simulate_Pop

  ■   Simulate_Push

  ■   System_Control


See Chapter 16. "Overview of Windows in 386 Enhanced Mode" and Chapter 17,
"Virtual Device Programming Topics" for general environment discussions.


Begin_Reentrant_Execution
────────────────────────────────────────────────────────────────────────────


Description

THIS IS A VERY DANGEROUS SERVICE. BE VERY CAREFUL WHEN CALLING IT. Most
virtual devices have no reason to use this service. Do NOT use this service
to avoid scheduling events on hardware interrupts.

It is intended to be used by devices that hook VMM Faults (re-entrant
processor exeptions) that must call non-asynchronous VMM or VxD services or
execute a VM. This would be valid to use, for example, if a VxD provided a
ring 0 software interrupt interface (although this is not recommended ─ You
should provide device services through the enhanced Windows dynamic-linking
mechanism). It would be INVALID to use this service during a hardware
interrupt (such as a timer or disk interrupt).


Entry

None


Exit

ECX = Old reentrancy count (must be passed to End_Reentrant_Execution)


Uses

ECX, Flags


End_Reentrant_Execution
────────────────────────────────────────────────────────────────────────────


Description

A VxD that calls Begin_Reentrant_Execution must call this service before
returning.


Entry

ECX = Reentrancy count returned from Begin_Reentrant_Execution


Exit

None


Uses

Flags


Hook_Device_Service
────────────────────────────────────────────────────────────────────────────


Description

This service allows one device to monitor or replace a device service.
extreme care must be taken here not to destroy the functionality of the
device whose routine is being monitored or replaced. This service also
allows VMM services to be hooked (the VMM is device 1).

Hooking a service is often useful for monitoring the activities of other
devices. For example, if a device needed to know whenever a VM was set into
background mode, it could use the following code:

  (Initialization code)
        mov    eax, Set_Time_Slice_Priority
        mov    esi, OFFSET32 My_Hook_Proc
        VMMcall Hook_Device_Service
        jc    Error!
        mov    [Real_Proc], esi

  BeginProc My_Hook_Proc
        test    eax, VMStat_Background
        jz    SHORT MHP_Chain
        pushad
        (Do something here)
        popad
  MHP_Chain:
        jmp     [Real_Proc]
  EndProc My_Hook_Proc

Every time a VxD calls Set_Time_Slice_Priority, the My_Hook_Proc procedure
will be called. The hook procedure should normally chain to the actual
device or VMM service although this is not required. Also, be sure to save
and restore any registers in your hook procedure.

You will notice that the sample initialization code moves
Set_Time_Slice_Priority into EAX. Remember, services are defined as EQUATES,
not external procedure references. Thus, Set_Time_Slice_Priority is just a
number. (VMM device ID

Your hook must preserve all registers that are not modified by the service
you have hooked. Also, if flags are passed as an entry or exit parameter,
your hook procedure must also preserve the flags.

Be careful about hooking C calling convention (stack-based) services. If you
want to examine the "back end" of a C calling convention service, you will
need to copy the entire parameter stack frame before calling the actual
service.

More than one VxD can hook a device service. The last hook installed will be
the first one called.


Entry

EAX = Device ID  ESI = New procedure


Exit

If carry clear then  ESI = Old dynalink procedure else  ERROR. Invalid
Device or Service number


Uses

ESI, Flags


Hook_Device_V86_API, Hook_PM_Device_API
────────────────────────────────────────────────────────────────────────────


Description

These services allow a VxD to hook another virtual device's V86 or protected
mode API interface.You are responsible for chaining to the real API handler.
Be careful to preserve the EBX and EBP registers when calling the next
handler in the chain.

Most VxDs will never need to hook another virtual device's API procedure.
These services are provided mainly as a mechanism for devices that may be
developed in the future to intercept API calls to other virtual devices. For
example, a new version of the Virtual Mouse Device may need to intercept
calls to the Virtual Display Device so that it can save and restore the
mouse cursor. In such a case, these services could be used.


Entry

EAX = Device ID ESI = Offset of new API handler


Exit

If carry clear then  ESI = Offset of previous API handler (used to chain to
next  handler) else  ERROR: Device does not support API interface


Uses

ESI, Flags


Callback

EBX = Current VM handle EBP -> Client register structure (Same parameters as
standard API entry point)


Map_Flat
────────────────────────────────────────────────────────────────────────────


Description

This service provides a convenient way of converting a SEGMENT:OFFSET or
SELECTOR:OFFSET pair into a linear address. Map_Flat works only for the
current VM. It determines whether the value passed to it is a V86 segment or
a PM selector by the execution mode of the current VM. This allows VxDs to
use identical code for PM and V86 handlers. For example, assume a VxD wanted
to simulate MS-DOS reads in both V86 and protected mode. It would hook both
the V86 and PM int chains with the same procedure:

  VxD_DOS_Read_Hook:
    cmp [ebp.Client_AH], 3Fh ; Q: Is it a read
    jne SHORT VxD_DRH_Reflect ;    N: Reflect it
        ;    Y: DS:DX -> Read buffer
    mov ax, (Client_DS SHL 8) + Client_DX
    VMMcall Map_Flat  ; EAX = Lin addr of DS:DX
    . . .
    (Do something useful here)
    . . .
    clc    ; Eat this int 21h
    ret
  VxD_DRH_Reflect:
    stc
    ret

Notice that the above procedure does not need to examine the VM's execution
state. By calling Map_Flat it converts the DS:DX pointer into a valid linear
address regardless of the VM's execution mode. If the VM was running a
32-bit protected mode application, it would convert the client's DS:EDX into
a linear address. For V86 and 16-bit protected-mode applications, it would
ignore the high word of the Client_EDX.

There is a macro called Client_Ptr_Flat that will generate this code
automatically. For the example above you would use:

  Client_Ptr_Flat eax, DS, DX

The first parameter specifies the 32-bit register to contain the linear
address. The second parameter specifies the client's segment. The third
parameter is optional and specifies the offset register (if blank then an
offset of 0 is assumed).

You must set AH to the equate for the client segment register you want to
map flat and AL to the offset register to add to the pointer base.
Alternately, if you do not want an offset added to the segment base you
should set AL to -1 (0FFh). This will just convert the segment to a linear
address.

Notice that during the running of 32-bit protected mode applications, or
while in "VxD_Exec" mode, pointers will use the 32-bit offset register.
Therefore, the above example would use DS:EDX when the CB_VM_Status field of
a VM's control block has either bit set in VMStat_Use32_Mask. This makes
Map_Flat work for V86, 16-bit protected mode, and 32-bit protected mode
programs.


Entry

AH = Offset in client structure of segment register to use. AL = Offset in
client structure of offset register to use  OR -1 if no offset to be added
to segment base


Exit

EAX = Ring 0 linear address


Uses

Flags, EAX


MMGR_SetNULPageAddr
────────────────────────────────────────────────────────────────────────────


Description

This call is used to set the physical address of the system nul page.

It can be called at device INIT time to set the address of a KNOWN
non-existant page in the system. This is usually called by the V86MMGR
device because he does memory scans and therefore has a good idea about what
a good page will be.


Entry

EAX is PHYSICAL address for NUL Page (Page number


Exit

None


Uses

Flags


Set_System_Exit_Code
────────────────────────────────────────────────────────────────────────────


Description

This service sets the DOS system call 4Ch return code value passed in AL on
the EXIT call made by the loader. Thus this sets the MS-DOS exit code that
Windows Enhanced Mode exits with. This service is intended for use by the
SHELL device.

Notice that this exit code is associated only with the EXIT of the the
system (i.e. the SYS VM).

Nice that in the case of an abnormal termination the LOADER may set its own
exit code, which will cause the one set with this service to be ignored.


Entry

AL == Exit Code to set


Exit

None


Uses

EDX,FLAGS


Simulate_Pop
────────────────────────────────────────────────────────────────────────────


Description

Returns the WORD or DWORD at the top of the current VM's client stack and
adds 2 or 4 to the client's SP.


Entry

None


Exit

EAX = Word popped from application's stack (high word 0 if use 16 app)


Uses

EAX, Client_ESP, Flags


Simulate_Push
────────────────────────────────────────────────────────────────────────────


Description

Pushes a WORD or DWORD onto the current VM's client stack and decrements the
VM's SP by 2 or 4.


Entry

If in V86 mode or 16 bit PM application then  AX = WORD to push else  EAX =
DWORD to push


Exit

(D)WORD pushed on application program's stack


Uses

Client_ESP, Flags


System_Control
────────────────────────────────────────────────────────────────────────────


Description

This service sends system control messages to all the VxD's and for some
messages, to parts of VMM as well. Notice that incorrect usage of the system
control messages can cause erratic behavior by the system. For example, only
the Shell device should initiate Create_VM and Destroy_VM messages. Also
notice that when a Set_Device_Focus message is done with a device ID of
zero, all devices with a settable focus must set their focus to the VM
indicated.

The valid System_Control messages are as follows:

╓┌──────────────────────┌────────────────────────────────────────────────────╖
Initialization         Sys_Critical_Init
                       Device_Init
                       Init_Complete
────────────────────────────────────────────────────────────────────────────
System VM creation     Sys_VM_Init
                       Sys_VM_Terminate

System VM destruction  System_Exit
(enhanced Windows      Sys_Critical_Exit
exit)

Other VM creation      Create_VM
                       VM_Critical_Init
                       VM_Init

Other VM destruction   VM_Terminate
                       VM_Not_Executable
Initialization         Sys_Critical_Init
                       Device_Init
                       VM_Not_Executable
                       Destroy_VM

VM state changes       VM_Suspend
                       VM_Resume
                       Set_Device_Focus

Special messages       Reboot_Processor
                       Debug_Query



The control calls that are valid for devices to issue are as follows:

Create_VM   (used by SHELL) Destroy_VM (used by SHELL) Set_Device_Focus


Entry

EAX = System control message EBX = VM handle (if needed by message)
ESI,EDI,EDX = message specific parameter, such as Device ID (for
Set_Device_Focus message) ECX register is used by this service and cannot
contain any parameter that  will be passed through to the devices.


Exit

Carry Set  Call failed Carry Clear  Call Succeeded  If Entry EAX = Create_VM
EBX = New VM handle created


Uses

Flags, EBX if Create_VM






Chapter 34  Shell Services
────────────────────────────────────────────────────────────────────────────

The Shell services provide a way for VxDs to communicate with the user. This
chapter presents descriptions of the Shell services in the following order:



  ■   SHELL_Event

  ■   SHELL_Get_Version

  ■   SHELL_Message

  ■   SHELL_Resolve_Contention

  ■   SHELL_SYSMODAL_Message


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


SHELL_Event
────────────────────────────────────────────────────────────────────────────


Description

This procedure posts an event occuring in the windows shell to VMDOSAPP.
This service is primarily for SHELL to WINOLDAPP communication. The VDD also
sends a couple messages to WINOLDAPP, other VxDs should have no use for this
service.


Entry

EBX is VM Handle for Event ECX is event # AX = wParam for event High 16 bits
EAX special boost flags ESI is callback procedure for event (==0 if none)
EDX is reference data for event callback


Exit

Carry Clear  Event placed in queue  EAX is "Event Handle" of event (only if
valid entry ESI   0) Carry Set  Event not placed  VMDOSAPP not present
Insufficient memory for placement


Callback

Carry Set  Event could not be placed in VMDOSAPP queue  EDX = reference data
NOTE THAT EBX != VM Handle of event! Carry Clear  Called when VMDOSAPP
signals event processing complete  EBP -> VMDOSAPP Client frame so registers
can be accessed  EDX = reference data  NOTE THAT EBX != VM Handle of event!



Uses

Flags, EAX


SHELL_Get_Version
────────────────────────────────────────────────────────────────────────────


Description

This procedure returns the version of the Shell VxD.


Entry

None


Exit

AH = Major version AL = Minor version Carry Flag clear


Uses

EAX, Flags


SHELL_Message
────────────────────────────────────────────────────────────────────────────


Description

This procedure is called to put up messages. Refer to SHELL.INC and the
Microsoft Windows Software Development Kit for information on message box
parameters.


Entry

EBX = VM Handle of VM responsible for message EAX = Message box flags (SEE
MB_xxxx in SHELL.INC) ECX -> NUL terminated Message Text EDI -> NUL
terminated caption Text == 0 for standard caption  -> NUL for No caption ESI
-> Callback procedure to call with response when dialog is finished == 0 if
no call back desired EDX = Reference data for callback


Exit

Carry Clear  EAX is "Event Handle" of message Carry Set  Message cannot be
displayed (insufficient memory)  Caller may wish to call
SHELL_SYSMODAL_MESSAGE in this csae.  SHELL_Sysmodal_Message will not fail.



Callback

Called when message box is complete EAX = Response code from dialog box (SEE
IDxx in SHELL.INC)  EDX = reference data EBX  VM Handle of message VM


Uses

Flags, EAX


SHELL_Resolve_Contention
────────────────────────────────────────────────────────────────────────────


Description

This procedure is called to resolve contention. It displays a dialog box in
which the user chooses which VM should get ownership of the device.


Entry

EAX = VM handle of current device owner EBX = VM handle of contending VM
(Must be Cur_VM_Handle) ESI -> 8 byte device name SPACE PADDED!!!


Exit

EBX = VM handle of contention winner If carry is set then contention could
not be resolved


Uses

EBX, Flags


SHELL_SYSMODAL_Message
────────────────────────────────────────────────────────────────────────────


Description

This procedure is called to put up SYSMODAL messages. Refer to SHELL.INC and
the Windows SDK for information on message box parameters.


Entry

EBX = VM Handle of VM responsible for message EAX = Message box flags (SEE
MB_xxxx in SHELL.INC)  NOTE THAT MB_SYSTEMMODAL MUST BE SET. ECX -> NUL
terminated Message Text EDI -> NUL terminated Caption Text == 0 for standard
caption  -> NUL for No caption


Exit

EAX = Response code from dialog box (SEE IDxx in SHELL.INC)


Uses

Flags, EAX






Chapter 35  Virtual Display Device (VDD) Services
────────────────────────────────────────────────────────────────────────────

These are the Virtual Display Device (VDD) services. See Chapter 18, "The
VDD and Grabber DLL," for a more detailed explanation.

The services are described in the following order:


  ■   VDD_Msg_BakColor

  ■   VDD_Msg_ClrScrn

  ■   VDD_Msg_ForColor

  ■   VDD_Msg_SetCursorPos

  ■   VDD_Msg_TextOut

  ■   VDD_Get_GrabRtn

  ■   VDD_Get_ModTime

  ■   VDD_Get_Version

  ■   VDD_Hide_Cursor

  ■   VDD_PIF_State

  ■   VDD_Query_Access

  ■   VDD_Set_HCurTrk

  ■   VDD_Set_VMType

  ■   VDD_Query_Access



35.1  Displaying a VM's Video Memory in a Window

There are several API services supplied to render efficiently a VM's video
memory into a window. These routines are called by the Grabber. Since the
Grabber runs in the Windows virtual machine, parameters are passed in the
Client Registers and in VM memory pointed to by the Client Registers.

The first step in updating windowed VMs is for the Shell to call Set_VMState
with a parameter indicating that the VM is to be windowed. This will enable
the VDD controller and memory state tracking and reporting of changes. When
the VM is no longer windowed, Set_VMState is called again. When the VMState
is not windowed, the Get_Mod call will always return no changes, and the
video update message will never be generated.

The Grabber has to be assured that the call to get the video memory is
consistent with the call to get the video state; for example, displaying a
mode 3 VM in mode 10 is inconsistent. To support this, the VM will not run
after a Get_Mod or Get_Mem call (an exception is hardware interrupts). The
VM resumes only after a Free_Mem or UnLock_App call. This way the VM's state
will not change during the process of window updating.

An exception is a VM that may update it's video memory during a timer
interrupt.

Notice that when a VM's video state changes, including controller state
changes such as cursor movement and memory modification, the VDD will send
WINOLDAPP a display update message. All the changes made to the video state
will accumulate and be reported by Get_Mod until a Clear_Mod call is made.
There will only be one display update message per Clear_Mod call.


35.2  Message Mode Services

When a Begin_Message_Mode control call is made, the VDD goes into a special
mode that allows the Shell device to use the VDD message services to output
text to the screen without changing the VM's video state. When the message
is complete, an End_Message_Mode control call is made that restores the
focus VM to the hardware.


VDD_Msg_BakColor
────────────────────────────────────────────────────────────────────────────


Description

After calling Begin_Message_Mode, this service sets up the background
attribute.


Entry

EAX = Color (for EGA/VGA driver, a text mode attribute) EBX = VM handle


Exit

None


Uses

Flags


VDD_Msg_ClrScrn
────────────────────────────────────────────────────────────────────────────


Description

This routine is called by the Shell to initialize the screen for putting up
messages. If the focus VM is the current VM, it will clear the screen
immediately. Otherwise, the screen will be initialized when the focus
changes. A Begin_Message_Mode device control must be issued before this
service is used.


Entry

EBX = VM handle EAX = background attribute


Exit

EAX = width in columns EDX = height in rows


Uses

Flags, EAX, EDX


VDD_Msg_ForColor
────────────────────────────────────────────────────────────────────────────


Description

After calling Begin_Message_Mode, this service sets up the foreground
attribute.


Entry

EAX = Color (for EGA/VGA driver, a text mode attribute) EBX = VM handle


Exit

None


Uses

Flags


VDD_Msg_SetCursPos
────────────────────────────────────────────────────────────────────────────


Description

After calling Begin_Message_Mode, this routine sets the cursor position.


Entry

EAX = row EDX = column EBX = VM handle


Exit

None


Uses

Flags


VDD_Msg_TextOut
────────────────────────────────────────────────────────────────────────────


Description

After calling Begin_Message_Mode and setting up the foreground and
background colors, this service puts characters on the screen.


Entry

ESI = address of string ECX = length of string EAX = row start EDX = column
start EBX = VM handle


Exit

None


Uses

Flags


35.3  Miscellaneous VDD Services
────────────────────────────────────────────────────────────────────────────

The services discussed in this section provide other VDD functions not
easily catagorized, such as hiding the cursor. They are provided here in
alphabetical order.


VDD_Get_GrabRtn
────────────────────────────────────────────────────────────────────────────


Description

This service returns the address of video grab routine. The grab routine is
called by the Shell device when the appropriate hot key is pressed by the
user. It makes a copy of the visible screen and controller state of the
current VM. That copy is then accessible via the GRB_Get_GrbState and
GRB_Get_GrbMem services.


Entry

None


Exit

ESI = address of grab routine


Uses

Flags, ESI


VDD_Get_ModTime
────────────────────────────────────────────────────────────────────────────


Description

This routine is used to determine if any video activity has occurred. The
poll device uses it to determine if the VM is idle.


Entry

EBX = VM handle


Exit

EAX = System Timer at last video modification


Uses

Flags, EAX


VDD_Get_Version
────────────────────────────────────────────────────────────────────────────


Description

This service returns the version number and device ID.


Entry

None


Exit

ESI = ptr to 8 byte ID string AH = major version AL = minor version Carry
Flag clear


Uses

Flags, AX, ESI


VDD_Hide_Cursor
────────────────────────────────────────────────────────────────────────────


Description

This service hides/shows the cursor in a window. If EAX is nonzero, then
this service sets a hide cursor flag or else clears the flag. This is so
that, if the mouse is using a hardware cursor, it can turn off that cursor
while the VM is windowed (since the VM will no longer own the mouse).


Entry

EAX = 0 if cursor SHOULD be displayed in a window   0 if cursor SHOULD NOT
be displayed in a window  EBX = control block pointer


Exit

None


Uses

Flags


VDD_PIF_State
────────────────────────────────────────────────────────────────────────────


Description

This service informs the VDD about PIF bits for the VM just created.


Entry

EBX = VM handle AX = PIF bits


Exit

None


Uses

Flags


VDD_Query_Access
────────────────────────────────────────────────────────────────────────────


Description

Returns error if there is an attempt by the specified VM by the specified VM
to access the video memory. This is used by the Virtual Mouse Device to
decide whether or not to turn off the mouse cursor when a VM no longer has
the mouse focus. This service should only be called in a VM Event just
before returning to the VM, e.g. Exec_Int to the mouse driver.


Entry

EBX = VM Handle


Exit

If video access OK  Carry Flag Clear else  Carry Flag Set


Uses

Flags Returns error if an attempt


VDD_Set_HCurTrk
────────────────────────────────────────────────────────────────────────────


Description

This service sets flag passed to VMDOSAPP indicating that VMDOSAPP should
maintain the cursor position within the display window for this application.
This is called by the Keyboard driver when a keyboard interrupt is simulated
into a VM.


Entry

EBX = VM handle


Exit

None


Uses

Flags


VDD_Set_VMType
────────────────────────────────────────────────────────────────────────────


Description

This service is used to inform the VDD of a VM's type. The parameter
explicitly passed is the windowed flag. The VM status flags, Exclusive and
Background, are implicity passed. This should be called prior to running the
VM and each time thereafter that any of the VM parameters are modified.
Notice that, for a system critical Set_Focus, this routine may not be called
before the Set_Focus. In that case, the VDD is responsible for doing an
implied Set_VMType (not windowed).


Entry

EAX = state flag (= nonzero if changing to windowed VM) EBX = VM handle
whose state is to change


Exit

None


Uses

Flags


VDD_Query_Access
────────────────────────────────────────────────────────────────────────────


Description

This service is used by the other VxDs when they want to access video
memory. The VxD should not access video memory unless this routine says it
is OK.


Entry

EBX = VM handle


Exit

if access is OK, carry flag = 0  else carry flag = 1


Uses

Flags






Chapter 36  Virtual Keyboard Device (VKD) Services
────────────────────────────────────────────────────────────────────────────

The Virtual Keyboard Device (VKD) provides services that support hot keys,
Message Mode key handling, and keyed input to VMs. The services are
presented in the following order:


  ■   VKD_Cancel_Hot_Key_State

  ■   VKD_Cancel_Paste

  ■   VKD_Define_Hot_Key

  ■   VKD_Define_Paste_Mode

  ■   VKD_Flush_Msg_Key_Queue

  ■   VKD_Force_Keys

  ■   VKD_Get_Kbd_Owner

  ■   VKD_Get_Msg_Key

  ■   VKD_Get_Version

  ■   VKD_Local_Disable_Hot_Key

  ■   VKD_Local_Enable_Hot_Key

  ■   VKD_Peek_Msg_Key

  ■   VKD_Reflect_Hot_Key

  ■   VKD_Remove_Hot_Key

  ■   VKD_Start_Paste


These are protected-mode API services used by WINOLDAP to send keys to a
windowed VM.

See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


VKD_Cancel_Hot_Key_State
────────────────────────────────────────────────────────────────────────────


Description

This service causes the VKD to exit the hot key state.


Entry

None


Exit

Keys will start being passed into the focus VM again


Uses

None


VKD_Cancel_Paste
────────────────────────────────────────────────────────────────────────────


Description

This service cancels the paste that was started in the VM with
VKD_Start_Paste.


Entry

EBX is VM handle


Exit

None


Uses

Flags


VKD_Define_Hot_Key
────────────────────────────────────────────────────────────────────────────


Description

This service defines a hot key notification routine. Hot keys are detected
by ANDing the shift state mask with the global shift state, then comparing
the resulting state with the shift state compare value. If this matches, and
the key code matches, then the callback routine is called with the specified
reference data in EDX.


Entry

AL = scan code of the main key AH = 0, if normal code AH = 1, if extended
code (ExtendedKey_B) AH = 0FFh, if either (AllowExtended_B) EBX = shift
state  high word is mask that is ANDed with the global shift state  when
checking for this hot key; low word is masked shift state  compare value.
Equates for common shift mask and compare values are    defined in VKD.INC:
HKSS_Shift for either shift key      HKSS_Ctrl for either control key
HKSS_Alt for either ALT key    The macro ShiftState is also defined to load
EBX with the mask    and compare value. e.g.,      ShiftState <SS_ALT +
SS_Toggle_mask>, SS_RAlt    loads EBX so that the hot key will only be
recognized when the    Right ALT key is held down.    VKD>INC also defines
"SS_" equates for the different shift state    bits and common combinations
of bits. CL = flags  CallOnPress - Call callback when key press is detected
CallOnRelease - Call callback when key release is  detected  (keyboard may
still be in hot-key hold  state)  CallOnRepeat - Call callback when repeated
press is  detected  CallOnComplete - Call callback when the hot key state is
ended(all shift modifier keys are  released) or when a different hot key is
entered (i.e. pressing ALT 1 2, if both  ALT+1 and ALT+2 are defined hot
keys,  then ALT+1's callback will be called  before ALT+2's to indicate that
the ALT+1  is complete even though the ALT key is  still down)  CallOnUpDwn
- Call on both press and release  CallOnAll  - Call on press, release and
repeats  PriorityNotify - Used with one of the call options to  specify that
the callback can only be  called when interrupts are enabled and the
critical section is un-owned  Local_Key - Key can be locally
enabled/disabled

ESI = offset of callback routine EDX = reference data EDI = maximum
notification delay if PriorityNotify is set,  0, means always notify
(milliseconds)


Exit

If Carry clear then  EAX = definition handle else the definition failed (no
more room)


Uses

Flags


Callback

Called when hot key is detected, and detection meets mask requirements.
(CallOnPress, CallOnRelease, CallOnRepeat, CallOnUpDwn, or CallOnAll)  AL =
scan code of key  AH = 0, if key just pressed (Hot_Key_Pressed)  = 1, if key
just released (Hot_Key_Released)  = 2, if key is an auto-repeat press
(Hot_Key_Repeated)  = 3, hot key state ended (Hot_Key_Completed)  EBX is hot
key handle  ECX = global shift state  EDX is reference data  EDI = elapsed
time for delayed notification (milliseconds) (normally 0, but if
PriorityNotify is specified then this value could be larger) This procedure
can modify EAX, EBX, ECX, EDX, ESI, EDI, and Flags


VKD_Define_Paste_Mode
────────────────────────────────────────────────────────────────────────────


Description

This service selects the VM's paste mode, whether INT 16 pasting can be
attempted or not. Some applications hook INT 9 and do things that will not
allow pasting to be done through INT 16H. Normally, VKD can detect this by
setting a timeout to see if any INT 16s are being done by the application,
and if not, then switching to INT 9 paste. But, some applications may do
some INT 16s, in which case the paste would be broken. Therefore, this
service is provided to allow the Shell device to force a VM into INT 9
paste, based only on a PIF bit.


Entry

AL = 0 allow INT 16 paste attempts AL = 1 force INT 9 pasting EBX = VM
handle


Exit

None


Uses

Flags


VKD_Flush_Msg_Key_Queue
────────────────────────────────────────────────────────────────────────────


Description

This service flushes any available keys from the special message mode input
buffer.


Entry

EBX = VM handle


Exit

Input buffer has been cleared


Uses

Flags


VKD_Force_Keys
────────────────────────────────────────────────────────────────────────────


Description

This service forces scan codes into the keyboard buffer that look exactly
like they had been typed on the physical keyboard. These keys will be
processed in the context of the focus VM.


Entry

ESI points to a buffer of scan codes ECX is # of scan codes in the buffer


Exit

If the keyboard buffer was overflowed, then  Carry set  ECX is # of
remaining scan codes that did not fit


Uses

ECX,Flags


VKD_Get_Kbd_Owner
────────────────────────────────────────────────────────────────────────────


Description

This service gets the VM Handle of the keyboard focus VM.


Entry

None


Exit

EBX = VM Handle of keyboard owner


Uses

Flags, EBX


VKD_Get_Msg_Key
────────────────────────────────────────────────────────────────────────────


Description

This service returns the next available key from the special message mode
input buffer and removes it from the buffer. If no key is available, then it
returns with the Z flag set. (This is not a blocking read!)


Entry

EBX = VM handle


Exit

Z flag clear, if key was read  AL = scan code  AH = modifier flags  MK_Shift
- a SHIFT key is down  MK_Ctrl - a CTRL key is down  MK_Alt - an ALT key is
down  MK_Extended - the key is an extended key  Z flag set, if no key
available


Uses

EAX, Flags


VKD_Get_Version
────────────────────────────────────────────────────────────────────────────


Description

This service gets the VKD version number.


Entry

None


Exit

AH = major, AL = minor Carry Flag clear


Uses

EAX, Flags


VKD_Local_Disable_Hot_Key
────────────────────────────────────────────────────────────────────────────


Description

This service disables a hot key in the specified VM. It is only allowed on
hot keys which were declared with the Local_Key bit set in CL.


Entry

EAX is hot key handle EBX is VM handle


Exit

None


Uses

Flags


VKD_Local_Enable_Hot_Key
────────────────────────────────────────────────────────────────────────────


Description

This service enables a hot key in the specified VM. It is only allowed on
hot keys which were declared with the local key bit set in CL.


Entry

EAX is hot key handle EBX is VM handle


Exit

None


Uses

Flags


VKD_Peek_Msg_Key
────────────────────────────────────────────────────────────────────────────


Description

This service returns the next available key from the special message mode
input buffer without removing it from the buffer. If no key is available,
then it returns with the Z flag set.


Entry

EBX = VM handle


Exit

Z flag clear, if key available  AL = scan code  AH = modifier flags
MK_Shift - a shift key is down  MK_Ctrl - a control key is down  MK_Alt - an
alt key is down  MK_Extended - the key is an extended key Z flag set, if no
key available


Uses

EAX, Flags


VKD_Reflect_Hot_Key
────────────────────────────────────────────────────────────────────────────


Description

This service reflects a hot key into a specified VM and exits the hot key
state. This service is normally called by a hot key notification callback
routine. It enables the callback to send the hot key into a VM and pretend
that it wasn't really recognized as a hot key. VKD will simulate the
required key strokes to get the VM into the state of this specified shift
state, then it will simulate the key strokes for the hot key itself, and
finally simulate key strokes to get the VM to match the current global shift
state.


Entry

EAX is hot key handle EBX is VM handle CX is required shift state


Exit

Hot key has been reflected, and VKD is no longer in hot key state


Uses

Flags


VKD_Remove_Hot_Key
────────────────────────────────────────────────────────────────────────────


Description

This service removes a defined hot key.


Entry

EAX is hot key definition handle to be removed


Exit

None


Uses

Flags


VKD_Start_Paste
────────────────────────────────────────────────────────────────────────────


Description

This service puts a VM into paste mode by simulating keyboard activity with
keystrokes taken from the specified paste buffer. Depending on the mode set
with the service VKD_Define_Paste_Mode (default is to try INT 16 pasting),
VKD waits for the VM to poll the keyboard BIOS through its INT 16 interface.
If the VM does keyboard input through the BIOS, then VKD will simulate the
keyboard input at this high level (plugging in ASCII codes.) If the VM fails
to perform any INT 16s within in a timeout period, or the mode has been set
to avoid INT 16 pasting, then VKD will simulate the necessary hardware
interrupts to perform the pasting. Physically typed hot keys are still
processed while pasting is in progress.


Entry

EAX is linear address of paste buffer  the paste buffer contains an array of
key structures:  OEM_ASCII_value db ?  scan_code db ?  shift_state dw ?
shift state bits are:  0000000000000010b shift key depressed
0000000000000100b ctrl key depressed The scan code should be FFh and the
shift state FFFFh, if VKD should convert the key to a ALT+numpad sequence.
(this information is identical to what is given by the Window's keyboard
routine OEMKeyScan)   EBX is VM handle  ECX is number of paste entries in
the paste buffer  ESI is call back address (can be 0)  EDX is reference data



Exit

Carry clear paste is started  Carry set paste failed, unable to allocate
memory for buffer copy


Uses

Flags


Callback

Called when paste is completed or cancelled   EAX is completion flags
Paste_Complete - paste completed successfully   Paste_Aborted - paste
cancelled by user   Paste_VM_Term - paste aborted because VM terminated
EBX is VM handle of VM that was receiving the paste   EDX is reference data
Procedure can modify EAX, EBX, ECX, EDX, ESI, EDI, and Flags






Chapter 37  Virtual PIC Device (VPICD) Services
────────────────────────────────────────────────────────────────────────────

The Virtual Programmable Interrupt Controller Device (VPICD) routes hardware
interrupts to other virtual devices, provides services that allow virtual
devices to request interrupts, and simulates hardware interrupts into
virtual machines. See Chapter 16, "Overview of Windows in 386 Enhanced
Mode," and Chapter 17, "Virtual Device Programming Topics," for general
discussions of the VPICD.

Peripherals, such as disk drives and COM ports, use hardware (physical)
interrupts to notify software of changes in their status.

The topics in this chapter are presented in the following order:


  ■   Default Interrupt Handling

  ■   Virtualizing an IRQ

  ■   Virtualized IRQ Callback Procedures

  ■   VPICD Services

  ■   Grabber




37.1  Default Interrupt Handling

The most basic function of VPICD is to emulate the functions of the physical
interrupt controller (PIC). This entails reflecting interrupts into virtual
machines and simulating I/O such as recognizing when a VM issues an EOI (End
Of Interrupt), reading the mask register, etc. When VPICD is initialized, it
sets up a default interrupt handler for every Interrupt ReQuest (IRQ). These
handlers determine which VM an interrupt should be reflected into, and they
arbitrate conflicts between virtual machines that attempt to unmask the same
interrupt.

An interrupt that is unmasked when enhanced Windows is initialized is
considered a global interrupt. A global interrupt will always be reflected
into the currently executing virtual machine, and any VM can mask or unmask
the IRQ. If a virtual machine unmasks an IRQ that was masked when the
enhanced Windows environment was initialized, it will own that IRQ. All
interrupts for an owned IRQ will be reflected only to the IRQ's owner. If
another virtual machine attempts to unmask the interrupt, the second VM will
be terminated and the user will see a dialog box that tells him to reboot
his computer.

It is important to remember that this is only the default behavior of VPICD.
If another virtual device virtualizes an IRQ it is up to the device that
virtualized the interrupt to determine which VMs receive interrupts and
arbitrate conflicts. Once an IRQ is virtualized, VPICD's default handling
for that IRQ stops.


37.2  Virtualizing an IRQ

When a virtual device needs to hook a specific IRQ (Interrupt ReQuest), it
must ask VPICD for permission. If another device has already virtualized the
IRQ, then the call will fail if either of the VxDs is unable to share the
IRQ (both must have the Can_Share option set for two VxDs to use the same
IRQ).

When a VxD calls VPICD_Virtualize_IRQ, it passes a pointer to a structure
called an IRQ Descriptor that contains the number of the IRQ and the address
of several callback procedures. This structure is included in the file
VPICD.INC:

  VPICD_IRQ_Descriptor  STRUC
   VID_IRQ_Number       dw   ?
   VID_Options          dw   0
   VID_Hw_Int_Proc      dd   ?
   VID_Virt_Int_Proc    dd   0
   VID_EOI_Proc         dd   0
   VID_Mask_Change_Proc dd   0
   VID_IRET_Proc        dd   0
   VID_IRET_Time_Out    dd   500
  VPICD_IRQ_Descriptor  ENDS

The VID_IRQ_Number contains the number of the IRQ the VxD wishes to
virtualize. VID_Options is a bit field that is used to specify special
options. The next five fields specify the address of various callback
procedures. The final field determines the maximum amount of time in
milliseconds that VPICD will allow before the interrupt is timed-out.
Time-outs are very important to prevent the enhanced Windows environment
from hanging while simulating a hardware interrupt.


37.3  Virtualized IRQ Callback Procedures

A virtual device may specify up to five callback procedures in its
IRQ_Descriptor structure. One of these, Hw_Int_Proc, is required. The other
callback procedures are optional and are simply used to inform a virtual
device whenever the state of the virtualized IRQ changes. For example, the
Virt_Int_Proc procedure will be called whenever an interrupt is simulated
into a VM; the Mask_Change_Proc is called whenever a virtual machine masks
or unmasks the interrupt, etc. Each of the callback procedures is described
in this section in detail and in alphabetical order. Callback procedures may
modify EAX, EBX, ECX, EDX, ESI, and Flags. Although they will be called with
interrupts disabled, they are allowed to enable them. If the procedures
perform a lot of processing, interrupts should be executed.


VID_Hw_Int_Proc
────────────────────────────────────────────────────────────────────────────


Description

The VID_Hw_Int_Proc procedure is called whenever a hardware interrupt
occurs. Notice that the procedure is just that, a procedure that returns
using a near return ─ not an IRET. Since the the VxD environment kernel is
single-threaded, the services that this procedure is allowed to call are
limited because it is possible for an interrupt to occur while executing in
the VMM. Therefore, many interrupt procedures will need to use the
Schedule_Call_Global_Event services to perform additional processing of an
interrupt. A typical VID_Hw_Int_Proc will service the physical device, call
VPICD_Phys_EOI to end the physical interrupt, and set the virtual IRQ
request for a specific virtual machine. Some VxDs may never request an
interrupt for a virtual machine and others may request more than one
interrupt per physical interrupt. In any case, every physical interrupt does
not need to be reflected 1-1 into a virtual machine.


Entry

Interrupts Disabled EAX = IRQ handle EBX = Current VM handle


Exit

If the IRQ is shareable then Carry Set indicates that the interrupt was not
handled by VxD Carry Clear indicates that the interrupt was handled by VxD


VID_EOI_Proc
────────────────────────────────────────────────────────────────────────────


Description

The VID_EOI_Proc callback is normally used for devices that are partially
virtualized. For example, the Virtual Mouse Device (VMD) lets the MS-DOS
mouse driver handle all I/O with the mouse hardware. The VMD just reflects
the interrupt to the VM that owns the mouse. Since it doesn't service the
device during the VMD_Hw_Int procedure, it can't call VPICD_Phys_EOI at this
point (since it's not the end of the interrupt). Once a virtual machine has
serviced the interrupt, it will issue an EOI and, at this point, the VMD
calls VPICD_Clear_Int_Request followed by VPICD_Phys_EOI. The default
interrupt routines need the VID_EOI_Proc callback for the same reason ─ they
have to wait for the VM to service the interrupting device before they
physically signal an EOI to the IRQ.


Entry

Interrupts Disabled EAX = IRQ handle EBX = Current VM handle


Exit

None


VID_Virt_Int_Proc
────────────────────────────────────────────────────────────────────────────


Description

The VID_Virt_Int_Proc callback can be useful for implementing critical
sections around a simulated hardware interrupt. A VxD will request an
interrupt, and that interrupt may be simulated at a later point in time.
This callback is issued at the point when the interrupt is actually being
simulated into the virtual machine. This call is made after the "point of no
return" has been passed. Therefore, it is impossible for a virtual device to
stop the interrupt once this call has been issued. A VxD that uses this
callback will usually also use the VID_Virt_IRET_Proc callback to detect the
end of the simulated interrupt.


Entry

Interrupts Disabled EAX = IRQ handle EBX = Current VM handle


Exit

None


VID_IRET_Proc
────────────────────────────────────────────────────────────────────────────


Description

This callback is useful for devices that must simulate large numbers of
interrupts in a short period of time. For example, the Virtual COM Device
will simulate an interrupt, allow one character to be read from the COM
port, and wait for the virtual machine to IRET before putting more data into
the virtual COM receive buffer. This is because many programs would crash if
too many bytes of data were queued and shovelled into the virtual machine
too quickly. The crash would occur because the program's stack would
overflow. For example, assume that a terminal program has an interrupt
routine that looks like this:

  push      ax               ; (Push AX, DX is the
       push      dx               ; minimum possible)
       (Read a byte from the COM port)
       mov       al, 20h          ; Non-Specific EOI
       out       20h, al          ; EOI the PIC
       sti                        ; Enable interrupts
       (Do other stuff)
       pop       dx
       pop       ax
       iret

This is a perfectly valid interrupt procedure and, in fact, it is very
common in actual terminal programs. Now consider what would happen if the
Virtual COM Device (VCD) had 500 bytes of data queued, and it did not use
the VID_IRET_Proc callback. When the VM reads a byte of data, VCD puts the
next byte of data into the receive buffer and request another interrupt.
When the terminal program executes the STI instruction, VPICD immediately
simulates another COM interrupt. This sequence of events is repeated 499
times, each time nesting an interrupt while in the terminal program's
interrupt routine. The problem is that the IRET frame on the stack requires
6 bytes per interrupt, and the 2 pushed registers take up 4 more bytes for a
total of 10 bytes per interrupt. Since we would nest 500 interrupts, 5K
bytes of stack space would be required.

Since this is obviously unacceptable, VCD waits for the terminal program to
IRET before simulating another interrupt. The Virtual Timer uses similar
logic to prevent shoving too many timer interrupts into a virtual machine.


Entry

Interrupts Disabled EAX = IRQ handle EBX = Current VM handle If carry is set
then interrupt timed-out


Exit

None


VID_Mask_Change_Proc
────────────────────────────────────────────────────────────────────────────


Description

The VID_Mask_Change_Proc is often used to detect contention for a device.
The default interrupt routines use this callback to detect conflicts with
nonglobal interrupts.


Entry

Interrupts Disabled EAX = IRQ handle EBX = Current VM handle ECX = 0 if VM
is unmasking IRQ,   0 if masking IRQ


Exit

None


37.4  VPICD Services
────────────────────────────────────────────────────────────────────────────

This section presents descriptions of the VPICD services in alphabetical
order.


VPICD_Call_When_Hw_Int
────────────────────────────────────────────────────────────────────────────


Description

You must call this procedure with interrupts disabled. This service enables
other VxDs to be notified when every hardware interrupt occurs. It is
intended to be used by the Virtual DMA Device (VDMAD) to detect when a DMA
transfer is complete. However, any VxD can use this service. It should be
noted though, that since your callback will be called for every hardware
interrupt, it could have a major performance impact on systems with devices
that interrupt frequently. Therefore, you should avoid using this service.

A callback installed by this service is responsible for chaining to the next
handler in the interrupt filter chain, and it must preserve the EBX register
for the next handler.

  Sample_Hook_Init:
        pushfd
        cli
        mov     esi, OFFSET32 My_Int_Hook
        VxDcall VPICD_Call_When_Hw_Int
        popfd
        mov     [Next_Int_Hook_Addr], esi
        clc
        ret

      My_Int_Hook:
        push    ebx
        (Do something useful here)
        pop     ebx
        jmp     [Next_Int_Hook_Addr]


Entry

ESI -> Procedure to call


Exit

ESI -> Procedure to chain to


Uses

ESI, Flags


Callback

EBX = Cur_VM_Handle


VPICD_Clear_Int_Request
────────────────────────────────────────────────────────────────────────────


Description

This service resets an IRQ request that was previously set by a call to
VPICD_Set_Int_Request. If the IRQ is being shared with another device, then
this service may not reset the virtual request if another device has also
set the virtual IRQ. However, the request will be cleared when all devices
that have called Set_Int_Request call this service.


Entry

EAX = IRQ handle EBX = VM handle


Exit

Virtual IRQ request is cleared


Uses

Flags


VPICD_Convert_Handle_To_IRQ
────────────────────────────────────────────────────────────────────────────


Description

This service returns the number of the IRQ for the IRQ handle in EAX.


Entry

EAX = IRQ Handle


Exit

ESI = IRQ Number


Uses

ESI, Flags


VPICD_Convert_Int_To_IRQ
────────────────────────────────────────────────────────────────────────────


Description

This service takes an interrupt vector number and returns the number of the
IRQ that is mapped to that interrupt. For example, INT 8 will typically be
converted to IRQ 0. However, VMs are allowed to remap the virtual PIC to any
interrupt vector they wish. Therefore, devices should never make assumptions
about to which interrupt vector a particular IRQ is mapped.


Entry

EAX = Interrupt vector number


Exit

If carry is clear then  EAX = IRQ number else  Interrupt vector not mapped
to any IRQ


Uses

None


VPICD_Convert_IRQ_To_Int
────────────────────────────────────────────────────────────────────────────


Description

This service accepts an IRQ number and returns an interrupt vector number
for a specified VM. For example, typically IRQ 0 will be converted to INT 8
on an IBM PC. However, VMs are allowed to remap the virtual PIC to any
interrupt vector they wish. Therefore, devices should never make assumptions
about to which interrupt vector a particular IRQ is mapped.


Entry

EAX = IRQ number ─ NOT HANDLE! EBX = VM handle


Exit

EAX = Interrupt vector


Uses

EAX, Flags


VPICD_Get_Complete_Status
────────────────────────────────────────────────────────────────────────────

Refer to VPICD_Get_Status for description.


VPICD_Get_IRQ_Complete_Status
────────────────────────────────────────────────────────────────────────────


Description

This service is similar to VPICD_Get_Complete_Status except that it takes an
IRQ number as a parameter instead of an IRQ handle. This is useful for
devices to inspect an IRQ before attempting to virtualize it or for
inspecting the state of another device's interrupt. Also, since it indicates
whether or not an IRQ has been virtualized already, it can be used by
devices to prevent conflicts when more than one device may want to use an
IRQ.


Entry

EAX = IRQ number


Exit

ECX = Status as described for VPICD_Get_Complete_Status

  If the carry flag is set then
      The IRQ has been virtualized
  else
      The IRQ has not been virtualized


Uses

ECX, Flags


VPICD_Get_Status
────────────────────────────────────────────────────────────────────────────


Description

These services return the status of a virtual IRQ for a specified VM. The
status returned in ECX is defined by equates in the VPICD.INC file.
VPICD_Get_Status will only return the Virtual In_Service and IRET_Pending
status bits. VPICD_Get_Complete_Status will return with all status bits
defined. The shorter version is supplied because it is much faster, and the
status returned contains the most commonly used information.


Entry

EAX = IRQ handle EBX = VM handle


Exit

ECX = Status flags (see equates VPICD.INI)

╓┌───────────────┌───────────────────────────────────────────────────────────╖
Bit             Description
────────────────────────────────────────────────────────────────────────────
0 = 1           A Virtual IRET is pending
1 = 1           The IRQ is virtually in service
2 = 1           The IRQ is physically masked
3 = 1           The IRQ is physically in service
4 = 1           VM has masked the IRQ
5 = 1           The Virtual IRQ is set (by any VxD)
6 = 1           The physical IRQ is set
7 = 1           Tha calling VxD's Virtual IRQ is set



Uses

ECX, Flags


VPICD_Get_Version
────────────────────────────────────────────────────────────────────────────


Description

This service returns the VPICD major and minor version numbers.


Entry

None


Exit

AH = Major version AL = Minor version EBX = Flags  Bit 0 = 1 - Master/Slave
PC/AT type configuration  0 - PC/XT type single PIC configuration  Other
bits reserved for future versions. ECX = Maximum IRQ supported (07H or 0FH)
Carry flag clear


Uses

EAX, EBX, ECX, Flags


VPICD_Phys_EOI
────────────────────────────────────────────────────────────────────────────


Description

Calling this procedure will end a physical interrupt and will allow further
hardware interrupts from the specified IRQ. Notice that an interrupt that is
physically in service will not suppress interrupts to "lower priority" IRQs,
since VPICD does not prioritize hardware interrupts. Therefore, it is
acceptable for an interrupt to be physically in service for an arbitrary
length of time.


Entry

EAX = IRQ handle


Exit

None


Uses

Flags


VPICD_Physically_Mask
────────────────────────────────────────────────────────────────────────────


Description

This service will mask the specified IRQ on the hardware PIC. This will
suppress all hardware interrupts on the IRQ until VPICD_Physically_Unmask or
VPICD_Set_Auto_Masking is called.


Entry

EAX = IRQ handle


Exit

IRQ is masked


Uses

Flags


VPICD_Physically_Unmask
────────────────────────────────────────────────────────────────────────────


Description

This service will unmask the specified IRQ on the hardware PIC regardless of
the mask state of virtual machines. This means that even if every VM has
masked the virtual IRQ, the physical IRQ will remain unmasked.


Entry

EAX = IRQ handle


Exit

IRQ is masked


Uses

Flags


VPICD_Set_Auto_Masking
────────────────────────────────────────────────────────────────────────────


Description

Automatic masking is the default state for every IRQ. It can be overridden
by VPICD_Physically_Mask/Unmask. When automatic masking is used, the state
of the physical mask is determined by the state of every virtual machine's
virtual mask. If at least one VM has the IRQ unmasked, then the physical IRQ
will remain unmasked. Otherwise, the IRQ will be masked on the hardware PIC.



Entry

EAX = IRQ handle


Exit

IRQ will be physically unmasked if at least one VM has unmasked the IRQ.


Uses

Flags


VPICD_Set_Int_Request
────────────────────────────────────────────────────────────────────────────


Description

This service sets the virtual interrupt request for the specified IRQ and
VM. It may cause an interrupt to be simulated immediately. However, in many
cases, the interrupt will not be simulated until a later point in time. The
interrupt will not be simulated immediately if:


  ■   The virtual machine has interrupts disabled.

  ■   The virtual machine has masked the IRQ.

  ■   A higher priority virtual IRQ is in service.

  ■   It is not possible to run the specified VM (it is suspended, etc).

  ■   There are other reasons the interrupt may be postponed.


However, since the interrupt may be simulated immediately, virtual devices
that have a virtual interrupt handler must be able to handle the case when
their virtual interrupt procedure is called before this service returns.

Setting an interrupt request is not a guarantee that the interrupt will ever
be simulated. For example, if the VM has masked the interrupt and never
unmasks it, the interrupt will never be simulated. Also, a call to
VPICD_Clear_Int_Request that is made before the virtual interrupt is
simulated will prevent the interrupt simulation.

It is important to keep in mind that VPICD simulates a level triggered PIC.
This means that once a virtual EOI occurs, another interrupt will be
simulated immediately unless the virtual interrupt request is cleared.


Entry

EAX = IRQ handle EBX = VM handle


Exit

Virtual IRQ request is set


Uses

Flags


VPICD_Test_Phys_Request
────────────────────────────────────────────────────────────────────────────


Description

This service will return with Carry set if the physical (hardware PIC)
interrupt request is set for the specified IRQ.


Entry

EAX = IRQ handle


Exit

Carry flag = Physical Interrupt Request state


Uses

Flags


VPICD_Virtualize_IRQ
────────────────────────────────────────────────────────────────────────────


Description

This is not an async service; it cannot be called during an interrupt. This
service is used to gain access to a specified virtual interrupt request. The
caller passes this procedure a pointer to the IRQ descriptor (the structure
declared in VPICD.INC) which specifies:


  ■   IRQ number (required)

  ■   Options

  ■   Hardware interrupt handler (required)

  ■   Virtual interrupt handler

  ■   Virtual EOI handler

  ■   Virtual mask change handler

  ■   Virtual IRET handler

  ■   Virtual IRET time-out (0 for no time-out)


For more information on the various options and parameters to this service
see Section 37.3 "Virtualizing an IRQ," earlier in this chapter. When this
service returns, if Carry is set, then the IRQ cannot be virtualized.
Otherwise, EAX contains an IRQ handle. This handle is used for all
subsequent communication with VPICD.

If every device that virtualizes the IRQ has the Can_Share option set then
the IRQ can be shared by up to 32 devices.


Entry

EDI -> VPICD_IRQ_Descriptor


Exit

If carry clear then  EAX = IRQ Handle else  Error ─ Handle already allocated
or invalid IRQ #


Uses

EAX , Flags






Chapter 38  Virtual Sound Device (VSD) Services
────────────────────────────────────────────────────────────────────────────

These two services enable VxDs to generate a warning beep or return the VSD
version number:


  ■   VSD_Bell

  ■   VSD_Get_Version


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


VSD_Bell
────────────────────────────────────────────────────────────────────────────


Description

This service is provided so that devices can generate a warning beep. This
is normally used when the user presses an invalid key or when an error
occurs. Notice that this service will produce a 1/2-second tone, but it will
then return immediately (it does not busy wait).


Entry

None


Exit

None


Uses

Flags


VSD_Get_Version
────────────────────────────────────────────────────────────────────────────


Description

This service returns the version number of the Virtual Sound Device.


Entry

None


Exit

AH = Major version number AL = Minor version number Carry flag clear


Uses

EAX, Flags






Chapter 39  Virtual Timer Device (VTD) Services
────────────────────────────────────────────────────────────────────────────

This chapter presents descriptions of the following VTD services:


  ■   VTD_Begin_Min_Int_Period

  ■   VTD_Disable_Trapping

  ■   VTD_Enable_Trapping

  ■   VTD_End_Min_Int_Period

  ■   VTD_Get_Interrupt_Rate

  ■   VTD_Get_Version

  ■   VTD_Update_System_Clock


See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.


VTD_Begin_Min_Int_Period
────────────────────────────────────────────────────────────────────────────


Description

This service is used by VxDs to ensure a minimum accuracy for system timing.
When this service is called, if the interrupt period specified is lower than
the current timer interrupt period, the interrupt period will be set to the
new frequency.

Until a matching VTD_End_Min_Int_Period call is made, the timer interrupt
period is guaranteed never to be slower than the value specified.

A VxD should call this service only once before calling
VTD_End_Min_Int_Period.

Typically the Begin/End_Min_Int_Period services are used by devices such as
execution profilers that need extremely accurate timing. VMM system time-out
services rely on the VTD to keep time. Therefore, more frequent timer
interrupts will allow the time out services to be more accurate.

────────────────────────────────────────────────────────────────────────────
WARNING

Fast timer interrupt periods can be very, very expensive in terms of total
system performance. For example, on some machines a timer interrupt of 1
millisecond will degrade total machine throughput by 10 percent and disk I/O
by up to 50 percent.
────────────────────────────────────────────────────────────────────────────


Entry

EAX = Desired interrupt period


Exit

If carry clear then  Interrupt period set else  Specified interrupt period
is not valid


Uses

Flags


VTD_Disable_Trapping
────────────────────────────────────────────────────────────────────────────


Description

This service will force VTD to stop I/O trapping on the timer ports for a
specified virtual machine. VTD_Enable_Trapping must be called once for every
call made to this service. By default, timer port trapping is enabled when a
VM is created.

It is sometimes necessary to disable temporarily I/O trapping for virtual
machine code that reads the timer in extremely tight timing loops. A good
example is the hard disk BIOS code that reads the ports hundreds of times
per disk transfer. The overhead for servicing the I/O traps would cause disk
performance to slow to a crawl.

────────────────────────────────────────────────────────────────────────────
WARNING

This service must be used very carefully. If a VM reprograms the timer while
port trapping is disabled, system timing will behave randomly. Only
"trusted" code should be executed when timer port trapping is disabled.
────────────────────────────────────────────────────────────────────────────

If this service is called N times, then VTD_Enable_Trapping must also be
called N times before trapping is reenabled. This allows nested calls to
this service by more than one VxD.


Entry

EBX = VM handle


Exit

None


Uses

Flags


VTD_Enable_Trapping
────────────────────────────────────────────────────────────────────────────


Description

This service must be called to re-enable timer I/O port trapping after
calling VTD_Disable_Trapping. Notice that this call must be made once for
every call to VTD_Disable_Trapping. Only when every disable call has been
matched by a call to this service will port trapping be reenabled.


Entry

EBX = VM handle


Exit

None


Uses

Flags


VTD_End_Min_Int_Period
────────────────────────────────────────────────────────────────────────────


Description

This service allows a device to "unrequest" a timer interrupt period that it
set earlier through the VTD_Begin_Min_Int_Period service. See the
documentation for VTD_Begin_Min_Int_Period earlier in this chapter for more
information on the proper use of this service.


Entry

EAX = Value passed earlier to Begin_Begin_Min_Int_Period


Exit

  If carry clear then
      Interrupt period request removed successfully
  else
      Specified interrupt period is not valid


Uses

Flags


VTD_Get_Interrupt_Period
────────────────────────────────────────────────────────────────────────────


Description

This service returns the current timer interrupt period.


Entry

None


Exit

EAX = Length of time between ticks in milliseconds


Uses

Flags


VTD_Get_Version
────────────────────────────────────────────────────────────────────────────


Description

This service returns the version number and the range of interrupt periods
allowable by this device.


Entry

None


Exit

EAX = Version number (AH = Major, AL = Minor) EBX = Fastest possible
interrupt period in milliseconds ECX = Slowest possible interrupt period in
milliseconds Carry flag clear


Uses

EAX, EBX, ECX, Flags


VTD_Update_System_Clock
────────────────────────────────────────────────────────────────────────────


Description

This service should only be called by the VMM. Devices should call the
Get_System_Time VMM service. The VMM will then call this service to update
the system clock.


Entry

None


Exit

None


Uses

Flags






Chapter 40  V86 Mode Memory Manager Device Services
────────────────────────────────────────────────────────────────────────────

The V86MMGR is responsible for managing memory in the Virtual 8086 portion
of each VM. It supports EMS and XMS, is responsible for allocating the base
memory for VMs when they are created, and translates APIs from
protected-mode applications into V86 calls for other VxDs.

See Chapter 16, "Overview of Windows in 386 Enhanced Mode," and Chapter 17,
"Virtual Device Programming Topics," for general environment discussions.
Other chapters that discuss memory management are Chapter 19, "Memory
Management Services," and Chapter 6, "Network Support," in the Microsoft
Windows Device Driver Adaptation Guide. Memory management is also discussed
in the Microsoft Windows Software Development Kit, Programming Tools.

The V86MMGR services are presented as follows:


  ■   Initialization Services

      V886MMGR_Get_Version

      V86MMGR_Allocate_V86_Pages

      V86MMGR_Set_EMS_XMS_Limits

      V86MMGR_Get_EMS_XMS_Limits

  ■   API Translation and Mapping Services

      V886MMGR_Set_Mapping_Info

      V86MMGR_Xlat_API

      V86MMGR_Load_Client_Ptr

      V86MMGR_Allocate_Buffer

      V886MMGR_Free_Buffer

      V86MMGR_Get_Xlat_Buff_State

      V86MMGR_Set_Xlat_Buff_State

      V86MMGR_Get_VM_Flat_Sel

      V86MMGR_Get_Mapping_Info

      V86MMGR_Map_Pages

      V86MMGR_Free_Page_Map_Region



40.1  Initialization Services

These services are used when a VM is created except for the
V86MMGR_Get_Version, which may be used anytime.


V86MMGR_Get_Version
────────────────────────────────────────────────────────────────────────────


Description

Returns the version of the V86MMGR VxD.


Entry

None


Exit

AH = Major version number AL = Minor version number Carry flag clear


Uses

EAX, Flags


V86MMGR_Allocate_V86_Pages
────────────────────────────────────────────────────────────────────────────


Description

This service is used by the SHELL VxD to set up the initial base memory of a
VM when it is created. It allocates the memory, maps it into the virtual
machine, and does a local Assign_Device_V86_Pages for the region allocated.



Entry

EBX = VM handle ESI = Desired size of VM address space in K bytes EDI  =
Minimum size of VM address space in K bytes ECX = Flags, see bit definitions
in V86MMGR.INC

────────────────────────────────────────────────────────────────────────────
NOTE

The ESI and EDI sizes include the 0-First_VM_Page region of V86 address
space.
────────────────────────────────────────────────────────────────────────────


Exit

If carry set then    ERROR: Could not allocate memory else    Memory
allocated and mapped into VM EAX = ACTUAL number of pages allocated and
mapped (size of VM). Notice that this size does not include the space from
0-First_VM_Page


Uses

EAX,Flags


V86MMGR_Set_EMS_XMS_Limits
────────────────────────────────────────────────────────────────────────────


Description

This service is used by the SHELL VxD to set the EMS and XMS limit
parameters for a VM.


Entry

EBX = VM handle to set limits of  EAX = Min EMS kilobytes  EDX = Max EMS
kilobytes  ESI = Min XMS kilobytes  EDI  = Max XMS kilobytes  ECX = Flag
bits, see V86MMGR.INC


Notes

To disable access to XMS or EMS memory, set Max = Min = 0 To set only one of
the two limits, set the OTHER Max = Min = -1 The XMS Limit does not include
the HMA.


Exit

If carry set then could not set limits    Insufficient memory for Min
allocation request    note that some of the limits may have been set. To
Find    out what happened, use V86MMGR_Get_EMS_XMS_Limits else limits set


Uses

Flags


V86MMGR_Get_EMS_XMS_Limits
────────────────────────────────────────────────────────────────────────────


Description

This service is used by the SHELL VxD to get the EMS and XMS limit
parameters for a VM.


Entry

EBX = VM handle to get limits of


Exit

EAX = Min EMS kilobytes (always a multiple of 4) EDX = Max EMS kilobytes
(always a multiple of 4) ESI = Min XMS kilobytes (always a multiple of 4)
EDI = Max XMS kilobytes (always a multiple of 4) ECX = 0 if access to the
HMA is disabled ECX = 1 if access to the HMA is enabled


Uses

EAX, ECX, EDX, ESI, EDI, Flags


40.2  API Translation and Mapping
────────────────────────────────────────────────────────────────────────────

One of the major roles of the V86MMGR is to provide a mechanism for other
VxDs to translate API calls made from application software running in
protected mode into the V86 portion of the virtual machine. The term "API
translation" is used in this document to describe the conversion of an API
call in protected mode into a corresponding V86 mode call. Because enhanced
Windows runs under a standard MS-DOS, MS-DOS and BIOS calls must be
reflected to V86 mode code to handle the call. There is a layer of code in
the DOSMGR device that converts protected mode MS-DOS calls into V86 calls.

The main translation service, V86MMGR_Xlat_API, is a simple interpreter that
copies data into a buffer in the V86 address space and converts pointers to
point to the copied data. Note that the data is copied. The memory is not
mapped into V86 memory by changing page tables.

Other services are provided to allocate buffer space, map memory into global
V86 address space, and perform other functions necessary for API
translation.

Translations services work only for the current VM, and most are only
available from the protected-mode portion of a VM.

Only one VxD should translate a given API call.

DPMI provides a way for an application to do translation without installing
a VxD to do the transition.


40.2.1  Basic API Translation

Many APIs require little or no translation. Others are extremely complex and
require a great deal of coding. The simplest API is one that has no
pointers. A software interrupt based API, in which all parameters are passed
in the EAX, EBX, ECX, EDX, ESI, EDI, and EBP registers and flags, requires
no special translation software. By default, enhanced Windows will reflect
an interrupt that is executed in protected mode into V86 mode. For example,
the BIOS printer interface (Int 17h) requires no translation code since all
APIs are register-based with no pointers.

However, most APIs have at least some calls that take pointers as
parameters. For example, to open a file through MS-DOS, you must point at
the name of the file to open with the DS:DX registers. Since the address
that a protected mode program will pass in DS:DX is not usually addressable
in the V86 portion of the VM, there must be code that copies the filename
into a buffer that is addressable in V86 mode so that MS-DOS can access the
filename.


40.2.2  Complex API Translation

Some APIs are too complex or their buffers are too large to be handled by
the V86MMGR_Xlat_API service. The MS-DOS Exec function takes a pointer to a
data structure that contains more pointers. This API requires special code
to translate the pointers in the data structure and to copy the data that
those pointers point to into V86 mode memory.

The MS-DOS read and write file functions can have buffers as large as 64K.
The typical V86MMGR translation copy buffer is 4K. Therefore, these calls
require code to divide the call into several smaller reads or writes in V86
mode.


40.2.3  Hooking the Interrupt

Since the translation code should be the last protected mode handler you
will need to hook the PM interrupt vector (using the Hook_PM_Int_Vector
service) during the Sys_Critical_Init or Device_Init phases of
initialization. All translation code should be initialized before the
Init_Complete phase of initialization so that the Exec_VxD_Int service
(provided by the VMM) can be used during this phase. Note that the V86MMGR
translation services (except for Set_Mapping_Info) should not be called
during Sys_Critical_Init or Device_Init.

By hooking the interrupt vector instead of using the Hook_PM_Int_Chain
service you will allow protected mode applications to hook software
interrupts "in front" of your translation code. This is very important for
the Windows kernel since it needs to monitor the activity of Windows
applications' API calls.


Sample Code

The code for a typical translation VxD looks like this:

  VxD_ICODE_SEG
  BeginProc My_Xlat_Init
   mov eax, My_Translation_Int_Number
   VMMcall Get_PM_Int_Vector
   mov [Chain_Segment], cx
   mov [Chain_Offset], edx
   mov esi, OFFSET32 My_Xlat_Procedure
   VMMcall Allocate_PM_Call_Back
   mov ecx, eax
   movzx edx, cx
   shr ecx, 16
   mov eax, My_Translation_Int_Number
   VMMcall Set_PM_Int_Vector
   clc
   ret
  EndProc My_Xlat_Init
  VxD_ICODE_ENDS

  VxD_CODE_SEG
  BeginProc My_Xlat_Procedure
   movzx eax, [ebp.Client_AH]
   cmp eax, My_Max_API_Number
   ja Chain_To_Next_Handler
   VMMcall Simulate_Iret
   mov edx, My_Trans_Script_Table[eax*4]
   VxDcall V86MMGR_Xlat_API
   ret
  Chain_To_Next_Handler:
   movzx ecx, [Chain_Segment]
   jecxz Reflect_To_V86_Now
   mov edx, [Chain_Offset]
   VMMcall Simulate_Far_Jmp
   ret
  Reflect_To_V86_Now:
   VMMcall Begin_Nest_V86_Exec
   mov eax, My_Translation_Int_Number
   VMMcall Exec_Int
   VMMcall End_Nest_Exec
   ret
  EndProc My_Xlat_Procedure
  VxD_CODE_ENDS

If the value in AH is not translated by this handler then it will be
reflected to the next protected mode interrupt handler. If there is not
another PM interrupt handler (code segment is zero) then the interrupt is
immediately reflected to V86 mode.

You will note that My_Xlat_Procedure calls the Simulate_Iret service before
it calls V86MMGR_Xlat_API. If you plan to "eat" an interrupt it is usually
best to call this service first. If the iret was simulated after the call to
V86MMGR_Xlat_API then any flags returned by the V86 interrupt handler would
be destroyed (an iret pops flags from the interrupt stack frame).


40.2.4  Mapping vs. Copying

Some VxDs need to use the paging mechanism of the 386 to map pages from
extended address space into the 1MB V86 address space of every virtual
machine. The Virtual NetBIOS Device uses the mapping services when an
asynchronous receive is issued so that the proper physical memory will be
updated regardless of which VM is currently running. When memory is mapped
using V86MMGR_Map_Pages it will be mapped to the same linear address in
every virtual machine. Thus it is best to avoid using these services.

Do not use mapping as an alternative to copying just because you think
mapping seems easier. It is faster to copy memory than to map it since the
memory manager does not need to perform any page table mapping and locking.
Mapping also uses a lot of address space (although it requires no memory).
The mapping services should only be used for APIs that require memory mapped
to the same address in every VM.

Note that the mapping services allow memory from one VM's V86 address space
to be mapped into all VMs at a common address. Don't use this for
interprocess communication. It will eat mapping space that may be required
by other devices. If you want to design an IPC interface, either make it
work for PM applications (which can share memory) or copy the data.


40.2.5  Writing Your Own Translation Procedures

Often, it is impossible to translate part or all of an API using the
supplied macro interpreter. Therefore you may need to write procedures that
do all or part of the translation. Examples of calls that require extra code
are the MS-DOS read and write commands and the get and set interrupt vector
commands. The MS-DOS commands to get and set interrupt vectors behave
differently in protected mode since they must hook the protected mode
interrupt vectors. These calls are never reflected to the "real" MS-DOS
running in V86 mode.

The MS-DOS read and write file commands can use a buffer as large as 64K.
Since the translation buffers can be as small as 4K, reads and writes must
be divided before being reflected to MS-DOS.

Since most APIs have some interfaces that can be handled by the
V86MMGR_Xlat_API script language and others that must be translated by
custom procedures you will probably want to dispatch to the custom
procedures using the Xlat_API_Jmp_To_Proc macro.

To adjust V86 segment registers you should leave the VM in PM_Exec_Mode and
change the Alt_Client registers. When in PM_Exec_Mode these registers
contain the V86 segment registers and stack pointer. They will contain the
PM segment registers and stack pointer when the VM is in V86_Exec_Mode.


40.2.6  Sample API Translation

This sample API is for an imaginary, incredibly simple network. The
functions allow you to connect to a server and send or receive data. Assume
that the network supports the following API from software interrupt 92h:


Function 0: Get version


Entry

AH = 0


Exit

AH = Major version AL = Minor version


Function 1: Get Server Name


Entry

AH = 1 DS:DX = Pointer to a 16 byte buffer to hold name


Exit

 None


Function 2: Connect To New Server


Entry

 AH = 2  DS:DX = Pointer to null terminated string that is name of server


Exit

 None


Function 3: Read/Write Data


Entry

 AH = 3  ES:BX = Pointer to command block with following structure:

╓┌───────┌─────┌─────────────────────────────────────────────────────────────╖
Offset  Size  Description
────────────────────────────────────────────────────────────────────────────
0       1     Command
1       2     Buffer size
3       4     Buffer pointer


Command field values:  0 = Read data from server  1 = Write data to server


Exit

None

Since function 0 is register based it requires no translation other than
reflecting the interrupt to V86 mode. Functions 1 and 2 both can be
translated by scripts using the V86MMGR_Xlat_API service. Function 3
requires a custom translation procedure.

  VxD_DATA_SEG
  Fctn_0_Script:
    Xlat_API_Exec_Int  92h
  Fctn_1_Script:  Xlat_API_Fixed_Len ds, dx, 16
   Xlat_API_Exec_Int  92h
  Fctn_2_Script:  Xlat_API_ASCIIZ   ds, dx
   Xlat_API_Exec_Int 92h
  Fctn_3_Script:
   Xlat_API_Jmp_To_Proc Trans_Fctn_3
  Copy_Command_Block_Script:
   Xlat_API_Fixed_Len es, bx, 7
   Xlat_API_Exec_Int  92h

  Xlat_Ptr_Table:
   dd OFFSET32 Fctn_0_Script
   dd OFFSET32 Fctn_1_Script  dd OFFSET32 Fctn_2_Script
   dd OFFSET32 Fctn_3_Script
  VxD_DATA_ENDS

  VxD_CODE_SEG
  BeginProc Translate_Sample_API
   movzx edx, [ebp.Client_AH]
   cmp edx, 3
   ja Chain_To_Next_Handler
   VMMcall Simulate_Iret
   mov edx, Xlat_Ptr_Table[edx*4]
   VxDcall V86MMGR_Xlat_API
   jc Translation_Error  ret
  Chain_To_Next_Handler:
   movzx ecx, [Chain_Segment]
   jecxz Reflect_To_V86_Now
   mov edx, [Chain_Offset]
   VMMcall Simulate_Far_Jmp
   ret
  Reflect_To_V86_Now:
   VMMcall Begin_Nest_V86_Exec
   mov eax, 92h
   VMMcall Exec_Int VMMcall
   End_Nest_Exec
   ret
  Translation_Error:
   Debug_Out "Unable to translate sample API"
   VMMjmp Crash_Cur_VM
  EndProc Translate_Sample_API

  BeginProc Trans_Fctn_3
   push fs
   push gs
   pushad
  ; Get pointer to command block
   mov ax, (Client_ES*100h)+Client_BX
   VxDcall V86MMGR_Load_Client_Ptr
  ; If command is invalid then fail the call
   mov al, BYTE PTR fs:[esi]
   cmp al, 1
   ja Can_Not_Translate
  ; Get buffer size and pointer from command block
   mov dx, fs
   mov gs, dx
   mov edx, esi movzx ecx, WORD PTR gs:[edx+1]
   mov fs, WORD PTR gs:[edx+5]
   movzx   esi, WORD PTR gs:[edx+3]
  ; Allocate a buffer, copying data if command is a write
   bt eax, 0
   VxDcall V86MMGR_Allocate_Buffer
   jc Can_Not_Translate
   mov DWORD PTR gs:[edx+3], edi
  ; Copy the command block and execute the interrupt
   push edx
   mov edx, OFFSET32 Copy_Command_Block_Script
   VxDcall V86MMGR_Xlat_API
   pop edx
   jc Can_Not_Translate
  ; Free the buffer, copying data if command is a read
   mov al, BYTE PTR gs:[edx]
   bt eax, 0
   cmc
   VxDcall V86MMGR_Free_Buffer
  ; Restore original pointer in command block
   mov WORD PTR gs:[edx+5], fs
   mov WORD PTR gs:[edx+3], si
   clc
  Trans_F3_Exit:
   popad
   pop gs
   pop fs
   ret
  Can_Not_Translate:  stc
   jmp Trans_F3_Exit
  EndProc Trans_Fctn_3

  VxD_CODE_ENDS


V86MMGR_Set_Mapping_Info
────────────────────────────────────────────────────────────────────────────


Description

This service must be called during the Sys_Critical_Init or Device_Init
phase of device initialization. It is used to define the minimum amount of
translation buffer and global V86 map address space that will be required.
VxDs such as the VNETBIOS use this service to ensure that there will be
adequate global page mapping space to map network buffers. By default the
translation copy buffer size is 4K and there are no global mapping pages.

Multiple VxDs may call this service. The V86MMGR will use the largest value
for each of the parameters when allocating buffer space. In other words, if
10 VxDs request a two-page copy buffer then the copy buffer will be two
pages (not 20).

Note that while a large copy buffer can speed up operations such as MS-DOS
reads, it requires extra memory to be allocated for every VM. Therefore, you
should try to get by with a copy buffer size of one page if possible.


Entry

AL = Minimum number of pages required for default copy buffer AH = Maximum
number of pages desired for default copy buffer BL = Minimum number of pages
required for global page mapping region BH = Maximum number of pages desired
for global page mapping region


Exit

None


Uses

Flags


V86MMGR_Xlat_API
────────────────────────────────────────────────────────────────────────────


Description

This service is actually a simple interpreter that executes scripts that are
created using macros defined in V86MMGR.INC. The macros are described in
detail below.

You can combine any of the macros, although you should keep in mind that
Xlat_API_Exec_Int and Xlat_API_Jmp_To_Proc both terminate interpretation of
the current script.

────────────────────────────────────────────────────────────────────────────
WARNING

You should always specify the exact length of a buffer or else strange
things may occur. For example, it is incorrect to translate an API that has
a maximum buffer size of 128 bytes by using the Xlat_API_Fixed_Len macro if
the buffer can be smaller than 128 bytes. This can cause bugs if the program
has data that is updated at interrupt time that is located past the end of
the buffer.
────────────────────────────────────────────────────────────────────────────

For example, assume a program has the following data:

  Buffer_Length db 64
  Buffer_Data db 64 dup (?)
  Time_Of_Day dd 0
  Other_Stuff db 500 dup (?)


Entry

EBX = Current VM handle EBP -> Client register structure EDX -> Script to
translate


Exit

EDX is destroyed If carry set then  Error while executing script else
Script has been executed successfully


Uses

EDX, Flags


Macro5

The following define the translation scripts.


Xlat_API_Exec_Int [Int Number]

Terminates the interpretation of the translation script and reflects the
specified interrupt into Virtual 8086 mode. When the interrupt returns then
it will return to the caller.

  DOS_No_Xlat_API:
   Xlat_API_Exec_Int 21h


Xlat_API_Fixed_Len [Segment], [Offset], [Length Constant]

Copies a fixed length buffer from extended memory into the translation
buffer and fixes up the V86 Seg:Offset.

This service will fail if there is not enough room in the translation buffer
to copy the data.

For example, the MS-DOS Get Current Directory function (AH=47h), must be
called with DS:SI pointing to a 64-byte buffer. The following script would
perform the appropriate translation:

  DOS_Get_Current_Directory_API:
   Xlat_API_Fixed_Len ds, si, 64
   Xlat_API_Exec_Int  21h


Xlat_API_Var_Len [Segment], [Offset], [Length Register]

Copies a variable number of bytes from extended memory into the translation
buffer. This is used for APIs where the caller places the buffer size in a
register.

This service will fail if there is not enough room in the translation buffer
to copy the data.

For example, the Int 10h write string function (AH=0Eh), must be called with
ES:BP pointing to the string to print and CX equal to the number of bytes to
display. The following script would translate this call:

  Int_10h_Write_String:
   Xlat_API_Var_Len  es, bp, cx
   Xlat_API_Exec_Int 10h


Xlat_API_Calc_Len [Segment], [Ptr_Off], [Calc_Proc_Addr]

Used to copy buffers that change in size. You must specify the
selector:offset register pair that points to the buffer and the name of a
procedure that will calculate the actual buffer size. The procedure will be
called with FS:ESI pointing to the buffer and must return with ECX equal to
the number of bytes to copy. The procedure must preserve all registers
except ECX.

This service will fail if there is not enough room in the translation buffer
to copy the data.

For example, the MS-DOS buffered keyboard input command (AH=0Ah) can have a
buffer size from 3 to 257 bytes long. The first byte of the buffer specifies
the length of the input buffer as follows:

╓┌─────────────────────────────────┌─────────────────────────────────────────►
Byte                              Contents
────────────────────────────────────────────────────────────────────────────
0                                 Maximum number of characters to read
                                  (1-255); this value must be set by the
                                  process before Function 0Ah is called.

1                                 Count of characters read.

2-(n+2)                           Actual string of characters read,
                                  including the carriage return;
                                  n = number of bytes read.



The translation code for this API would look something like this:

  VxD_DATA_SEG
  Buff_Keyboard_Input_API:
   Xlat_API_Calc_Len ds, dx, Calc_Input_Buff_Size
   Xlat_API_Exec_Int 21h
  VxD_DATA_ENDS

  VxD_CODE_SEG
  BeginProc Int_21_PM_To_V86_Translator

   cmp [ebp.Client_AH], 0Ah
   jne Not_Buffered_Keyboard_Input
   VMMcall Simulate_Iret
   mov edx, OFFSET32 Buff_Keyboard_Input_API
   VxDcall V86MMGR_Xlat_API
   ret

  EndProc Int_21_PM_To_V86_Translator

  BeginProc Calc_Input_Buff_Size

   movzx ecx, BYTE PTR fs:[esi]
   add ecx, 2
   ret

  EndProc Calc_Input_Buff_Size
  VxD_CODE_ENDS


Xlat_API_ASCIIZ [Ptr_Seg], [Ptr_Off]

Copies a null-terminated string into V86 memory and adjusts the V86 pointer
appropriately. Note that the string will not be copied back after the call
is complete.

This service will fail if there is not enough room in the translation buffer
to copy the string.

For example, the MS-DOS Open File With Handle function (AH=3Dh), must be
called with DS:DX pointing to the name of the file to open. The following
script could be used translate the API:

  DOS_Open_File_With_Handle:
   Xlat_API_ASCIIZ   ds, dx
   Xlat_API_Exec_Int 21h

The interpreter can copy multiple buffers. For example, the following
translation table translates the MS-DOS rename file call (AH = 56h):

  Rename_API:
   Xlat_API_ASCIIZ   ds, dx
   Xlat_API_ASCIIZ   es, di
   Xlat_API_Exec_Int 21h

The first instruction copies the null-terminated string (ASCIIZ string) that
DS:DX points to into the translation buffer in V86 memory, sets the V86 DS
to the translation buffer segment, and changes DX to the offset in the
buffer.

The second macro copies the ASCIIZ string that is pointed to by ES:(E)DI
into V86 memory and adjusts the pointer accordingly.

The final macro terminates the interpretation of the script and reflects an
Int 21h into the V86 portion of the VM. When the Int 21h returns, both
buffers will be freed.


Xlat_API_Jmp_To_Proc [Proc_Name]

Terminates the interpretation of the translation script and transfers
control to a user defined procedure. The procedure can completely handle the
API translation or can call V86MMGR_Xlat_API again. This can be useful for
APIs that have several sub-APIs such as the DOS IOCTL calls.

The procedure will be called with EBX equal to Current VM Handle, EBP
pointing to Client register structure, and EDX points to the next entry in
the translation script (if there is one). It must preserve every register
except for EDX. Therefore the procedure must preserve EAX, EBX, ECX, ESI,
EDI, EBP, DS, ES, FS, and GS.

Your procedure should return with the carry flag clear if the translation
was successful. Otherwise, it should return with carry set to indicate an
error.


Xlat_API_Return_Ptr [Ptr_Seg], [Ptr_Off]

Used for calls that return a pointer to a structure. For 16-bit protected
mode programs, if an appropriate selector does not exist to map the call,
then this service automatically creates one. For 32-bit protected mode
programs the selector returned will always be the V86MMGR_VM_Flat_Selector
and the offset will be adjusted. Note that although this macro is placed
before the Exec_Int macro in a translation script, the pointer is created
after the interrupt has been executed.

This service will fail if it can not create an appropriate LDT selector.

For example, this service is used to translate Int 15h with AH=C0h, which
returns a pointer in ES:BX that points to a hardware information structure
on PS/2 machines. The following script would return the appropriate pointer:


  Get_Machine_Info:
   Xlat_API_Return_Ptr es, bx
   Xlat_API_Exec_Int   15h


Xlat_API_Return_Seg [Ptr_Seg]

Used for calls that return a segment. If an appropriate selector does not
exist to map the call then this service automatically creates one. Note that
although this macro is placed before the Exec_Int macro in a translation
script, the selector is created after the interrupt has been executed.

This service will fail if it can not create an appropriate LDT selector.

For example, this service is used to translate Int 15h with AH=C1h, which
returns the segment of the EBIOS data area in ES. The following script would
return a selector that points to the EBIOS data area:

  Get_EBIOS_Selector:
   Xlat_API_Return_Seg es
   Xlat_API_Exec_Int   15h

Assume the program updates the Time_Of_Day field from the timer interrupt.
If the translation code copies 128 bytes of data starting with Buffer_Length
into V86 mode memory and while processing the call a timer interrupt
executes then the Time_Of_Day field will be incremented. However, when the
buffer is copied back the old time will be copied on top of the current
(correct) Time_Of_Day field.


V86MMGR_Load_Client_Ptr
────────────────────────────────────────────────────────────────────────────


Description

This service will load FS:ESI with the specified Client_Seg:Offset. If the
VM is running a 16-bit protected mode then the high word of the offset in
ESI will be zeroed. Otherwise, if the VM is running a 32-bit program or is
in VxD_Exec_Mode then the high word of ESI will not be zeroed. This allows
most translation procedures to operate correctly without the need to test
the execution mode of the current VM.

The value passed in AX should be formed from the Client Register Structure
equates. For example, to load the VM's DS:(E)DX you would use the following
code:

  mov ax, (Client_DS * 100h) + Client_DX
  VxDcall V86MMGR_Load_Client_Ptr
  (FS:ESI -> Same address as Client_DS:(E)DX).


Entry

VM must be in protected mode AH = Client segment register equate AL = Client
offset register equate EBX = Current VM Handle EBP -> Client register
structure


Exit

FS:ESI -> Client's buffer


Uses

FS, ESI, Flags


V86MMGR_Allocate_Buffer
────────────────────────────────────────────────────────────────────────────


Description

Allocates a portion of the current VM's translation buffer and optionally
copies data from the PM pointer in FS:ESI into the allocated buffer.

Note that this service will map fewer bytes than the value specified in the
ECX parameter if the length of the buffer extends past the FS segment limit.
Therefore, you need to preserve the value returned in ECX from this service
to use when deallocating the buffer using V86MMGR_Free_Buffer.

The buffers are maintained as a stack. Therefore, the last buffer allocated
must be the first buffer freed.


Entry

Current VM must be in protected mode EBX = Current VM Handle EBP -> Client
register structure ECX = Number of bytes to allocate FS:ESI = Pointer to
extended memory to copy If carry flag is set then  Source buffer will be
copied into V86 buffer else  Source buffer will not be copied into V86
memory


Exit

If carry set then  ERROR: Could not allocate buffer (out of space) else  ECX
= Actual number of bytes allocated (<= original ECX)  High WORD of EDI = V86
segment of translation buffer  Low WORD of EDI = Offset of allocated buffer



Uses

ECX, EDI, Flags


V86MMGR_Free_Buffer
────────────────────────────────────────────────────────────────────────────


Description

Deallocates a buffer that was allocated by the V86MMGR_Allocate_Buffer
service. It will optionally copy data from the translation buffer to the
buffer pointed to by FS:ESI.

The buffers are maintained as a stack. Therefore, the last buffer allocated
must be the first buffer freed.


Entry

Current VM must be in protected mode EBX = Current VM Handle EBP -> Client
register structure ECX = Number of bytes to free (returned from
Allocate_Buffer) FS:ESI = Pointer to extended memory buffer If carry flag is
set then  Buffer will be copied from V86 memory before buffer freed else
Buffer will not be copied


Exit

None


Uses

Flags


V86MMGR_Get_Xlat_Buff_State
────────────────────────────────────────────────────────────────────────────


Description

This service returns information about the current mapping buffer status.

────────────────────────────────────────────────────────────────────────────
WARNING

Always call this service to find the segment of the translation buffer.
Since the buffer can move at any time you should never make any assumptions
about the size or location of the buffer.
────────────────────────────────────────────────────────────────────────────


Entry

EBX = VM handle (any VM handle valid)


Exit

EAX = V86 segment of translation buffer (high word 0) ECX = Number of bytes
of buffer not in use EDX = Total size of buffer in bytes (max size 10000h)


Uses

EAX, EBX, ECX, Flags


V86MMGR_Set_Xlat_Buff_State
────────────────────────────────────────────────────────────────────────────


Description

This service is used to switch to an alternate mapping buffer. This feature
is provided for protected mode terminated-and-stay resident programs which
may need to switch to a private translation buffer before executing
protected mode MS-DOS calls since the default buffer may be full.

You should get the current translation buffer state, set the new state,
perform any MS-DOS call, and then set the state back to the original values.



Entry

EBX = VM handle (any VM handle valid) EAX = V86 segment of translation
buffer (high word 0) ECX = Number of bytes of buffer not in use EDX = Total
size of buffer in bytes (max size 10000h)


Exit

None


Uses

Flags


V86MMGR_Get_VM_Flat_Sel
────────────────────────────────────────────────────────────────────────────


Description

This service returns a selector that points to the base of the specified
VM's V86 address space. This is useful for 32-bit applications since this
selector can be used to point to any address in the VM's V86 address space.
The selector is writeable and has a limit of 11,000h bytes so that the high
memory area is also addressable.

The selector returned is in the specified VM's LDT. Therefore, the selector
is only valid to use when the VM is running (is the current VM).


Entry

EBX = VM handle (any VM handle is valid)


Exit

EAX = Selector with base at high linear addr of V86 memory (high word 0)


Uses

EAX, Flags


V86MMGR_Get_Mapping_Info
────────────────────────────────────────────────────────────────────────────


Description

This service will return information about the current page mapping areas.


Entry

None


Exit

CH = Number of pages reserved for global mapping (total) CL = Number of
pages available (not in use) for global mapping


V86MMGR_Map_Pages
────────────────────────────────────────────────────────────────────────────


Description

This service maps the specified buffer into every VM at the same address
using page mapping. If the contents of memory are changed in one VM, the
change will be reflected in the original buffer as well in all other VMs.


Entry

ESI -> Linear address to map ECX = Number of bytes to map


Exit

If carry flag is set then  ERROR: Could not map memory else  Memory is
mapped  ESI = Map handle (used to free the map region)  EDI = Linear address
of map buffer (


Uses

ESI, EDI, Flags


V86MMGR_Free_Page_Map_Region
────────────────────────────────────────────────────────────────────────────


Description

This service will "unmap" pages that were mapped by the V86MMGR_Map_Pages
service.


Entry

ESI = Map handle to free


Exit

Old map buffer address contains null memory ESI is undefined


Uses

ESI, Flags






Chapter 41  Virtual DMA Device (VDMAD) Services
────────────────────────────────────────────────────────────────────────────

The VDMAD is the Microsoft Windows virtual device providing DMA services
according to the VDS specifications. This VxD should be used in the enhanced
Windows environment rather than using the VDS services directely. By
default, it handles all programmed I/O for the DMA controllers and
arbitrates I/O to the physical DMA ports so that more than one VM can be
using the same DMA channels at the same time. In some cases, the default
handling of DMA channels is not desirable. To handle these cases, VDMAD
provides a number of services to enable another VxD to take control of the
virtualization of specific DMA channels.

VDMAD also provides some services that can be used by Bus Master devices
that have their own DMA controllers. These devices still need to be able to
lock and unlock DMA regions in memory and determine the physical addresses
of these regions. Bus Master devices can also make use of the buffer
services, if they cannot otherwise scatter/gather a linear region that is
not physically contiguous.

The VDMAD services available for Bus Master use are as follows:


  ■   VDMAD_Copy_From_Buffer

  ■   VDMAD_Copy_To_Buffer

  ■   VDMAD_Default_Handler

  ■   VDMAD_Disable_Translation

  ■   VDMAD_Enable_Translation

  ■   VDMAD_Get_EISA_Adr_Mode

  ■   VDMAD_Get_Region_Info

  ■   VDMAD_Get_Version

  ■   VDMAD_Get_Virt_State

  ■   VDMAD_Lock_DMA_Region

  ■   VDMAD_Mask_Channel

  ■   VDMAD_Release_Buffer

  ■   VDMAD_Request_Buffer

  ■   VDMAD_Reserve_Buffer_Space

  ■   VDMAD_Scatter_Lock

  ■   VDMAD_Scatter_Unlock

  ■   VDMAD_Set_EISA_Adr_Mode

  ■   VDMAD_Set_Phys_State

  ■   VDMAD_Set_Region_Info

  ■   VDMAD_Set_Virt_State

  ■   VDMAD_Unlock_DMA_Region

  ■   VDMAD_UnMask_Channel

  ■   VDMAD_Virtualize_Channel



VDMAD_Copy_From_Buffer
────────────────────────────────────────────────────────────────────────────


Description

This service allows another device to copy data from the VDMAD buffer to the
actual DMA region associated with the buffer. This service is called after
VDMAD_Request_Buffer, after a memory write transfer and before
VDMAD_Release_Buffer.


Entry

EBX = buffer ID ESI = region linear EDI = offset within buffer for start of
copy ECX = size


Exit

Carry clear  data copied from buffer into DMA region Carry set  AL = 0Ah
(DMA_Invalid_Buffer) - invalid buffer  id supplied  = 0Bh
(DMA_Copy_Out_Range) - (ESI + ECX) is  greater than buffer size


Uses

Flags


VDMAD_Copy_To_Buffer
────────────────────────────────────────────────────────────────────────────


Description

This service allows another device to copy data into the VDMAD buffer from
the actual DMA region associated with the buffer.This service is called
after VDMAD_Request_Buffer and before starting a memory read transfer.


Entry

EBX = buffer id ESI  = region linear EDI = offset within buffer for start of
copy ECX = size


Exit

Carry clear  data copied from DMA region into buffer Carry set  AL = 0Ah
(DMA_Invalid_Buffer) - invalid buffer  id supplied  = 0Bh
(DMA_Copy_Out_Range) - (ESI + ECX) is  greater than buffer size


Uses

Flags


VDMAD_Default_Handler
────────────────────────────────────────────────────────────────────────────


Description

Default DMA channel I/O callback routine. This routine receives
notifications of virtual state changes and handles setting up the physical
state to start DMA transfers.

  get virtual state
  If channel virtually unmasked then
      lock region
      If lock fails then
         request buffer
         If memory read operation then
             copy data to buffer
      set phyical state
      physically unmask channel


Entry

EAX = DMA handle EBX = VM handle


Exit

None


Uses

Anything


VDMAD_Disable_Translation
────────────────────────────────────────────────────────────────────────────


Description

This service disables the automatic translation done for the standard DMA
channels.It is necessary, if a V86 app or driver, or a PM app uses the DMA
services thru INT 4BH to determine actual physical addresses for DMA
transfers. A disable count is maintained, so a matching call to
VDMAD_Enable_Translation is required for each call to this service to
re-enable translation.


Entry

EAX = DMA handle EBX = VM Handle


Exit

Carry clear   automatic translation is disable for the channel Carry set
the disable count overflowed


Uses

Flags


VDMAD_Enable_Translation
────────────────────────────────────────────────────────────────────────────


Description

This decrements the disable count associated with a standard DMA channel. If
the disable count goes to 0, then automatic translation is re-enabled. See
VDMAD_Disable_Translation for further information.


Entry

EAX = DMA handle EBX = VM Handle


Exit

Carry clear   service completed successfully   Z-flag clear, if automatic
translation is re-enabled Carry set   attempt to enable when translation
already enabled


Uses

Flags


VDMAD_Get_EISA_Adr_Mode
────────────────────────────────────────────────────────────────────────────


Description

Get EISA extended mode - the hardware doesn't allow for reading the extended
mode for a channel, so VDMAD defaults to the ISA defaults (channels 0-3 are
byte channels and 5-7 are word channels with word addresses and counts) An
INI switch can specify an alternate setting.


Entry

EAX = Channel # (0..7) or  DMA Handle


Exit

CL = 0 - 8-bit I/O, with count in bytes CL = 1 - 16-bit I/O, with count in
words and adr shifted CL = 2 - 32-bit I/O, with count in bytes CL = 3 -
16-bit I/O, with count in bytes


Uses

ECX, Flags


VDMAD_Get_Region_Info
────────────────────────────────────────────────────────────────────────────


Description

Get information about the current region assigned to a DMA handle. This
information can be used by a handler to call the following services:


  ■   VDMAD_Unlock_DMA_Region

  ■   VDMAD_Release_Buffer

  ■   VDMAD_Copy_To_Buffer

  ■   VDMAD_Copy_From_Buffer



Entry

EAX = DMA handle


Exit

BL = buffer id BH = pages locked (0 = FALSE, else TRUE) ESI  = region linear
ECX = size in bytes


Uses

EBX, ECX, ESI


VDMAD_Get_Version
────────────────────────────────────────────────────────────────────────────


Description

Returns the version of the Virtual DMA Device


Entry

None


Exit

AH = Major version number AL = Minor version number ECX = Buffer size in
bytes (0, if not allocated; a buffer will always  be allocated, but it
doesn't happen until Device_Init) Carry flag clear


Uses

EAX, Flags


VDMAD_Get_Virt_State
────────────────────────────────────────────────────────────────────────────


Description

This service allows a channel owner to determine the current virtual state
of the channel. The virtual state consists of all the information necessary
to physically program the DMA channel for a DMA transfer (linear address of
target region, byte length of region, mode of transfer, and state of mask
bit and software request bit) This state information reflects how the VM
thinks the hardware is currently programmed.


Entry

EAX = DMA handle EBX = VM handle


Exit

If translation is enabled  ESI = high linear address of the user's DMA
region    (high linear is used so that the DMA can proceed    even if a
different VM is actually running at the    time of the transfer) Else  ESI =
physical byte address programmed (shifted left 1,    for word ports) ECX =
count in bytes DL= mode (same as 8042 mode byte with channel # removed
and DMA_masked & DMA_requested set as    appropriate:      DMA_masked
channel masked and not ready            for a transfer      DMA_requested
software request flag set) DH= extended mode (ignored on non-PS2 machines
that don't       have extended DMA capabilities)


Uses

ESI, ECX, EDX, flags


VDMAD_Lock_DMA_Region
────────────────────────────────────────────────────────────────────────────


Description

This service attempts to lock a region of memory for a DMA transfer. It is
called before a DMA transfer is started (before the physical state is set
for a channel and before it is unmasked.)

It first verifies that the region is mapped to contiguous pages of physical
memory.

Then it determines whether the region will result in a DMA bank (page)

wrap

    On AT(R) class machines each channel has a base address register and a
    page address register. The base address register is incremented after
    each byte or word transfered. If the increment of this 16 bit register
    results in the roll over from FFFFh to 0, then the transfer wraps to the
    start of the DMA bank because the page register is not updated. Normally
    MS-DOS watches for this condition and adjusts INT 13h parameters to
    split transfers to avoid this wrap, but MS-DOS doesn't know anything
    about the difference between linear and physical addresses under
    enhanced Windows, so VDMAD checks again to prevent wrap from occurring
    undesirably.

If all of these checks are okay, then the service calls the memory manager
to lock the physical pages.

────────────────────────────────────────────────────────────────────────────
NOTE

This routine does not check to see if the region is within some physical
maximum constraint. If the region is lockable, then it locks the memory, and
it is up to the caller to check to see if the physical region is acceptable.
If the region is not acceptable, then the caller should unlock the region
and perform a buffered DMA transfer.
────────────────────────────────────────────────────────────────────────────


Entry

ESI  = linear address of actual DMA region ECX = # of bytes in DMA region DL
  = 1b, if region must be aligned on 64K page boundary  = 10b, if region
must be aligned on 128K page boundary


Exit

Carry set, if lock failed   ECX = # of bytes that are lockable in the region
(starting from ESI)   AL = 1 (DMA_Not_Contiguous), region not contiguous  =
2 (DMA_Not_Aligned), region crossed physical  alignment boundary  = 3
(DMA_Lock_Failed), unable to lock pages ELSE  EDX = physical address of the
DMA region  the region has been locked


Uses

EAX, ECX, EDX, Flags


VDMAD_Mask_Channel
────────────────────────────────────────────────────────────────────────────


Description

This service physically masks a channel so that it will not attempt any
further DMA transfers.


Entry

EAX = DMA handle


Exit

None


Uses

Flags


VDMAD_Release_Buffer
────────────────────────────────────────────────────────────────────────────


Description

Release the VDMAD buffer assigned to a DMA channel from a previous
VDMAD_Request_Buffer call. This routine exits from a critical section and
the DMA buffer will now be available for other users. Any data in the buffer
is not automatically copied, so VDMAD_Copy_From_Buffer must be called if the
data is important.


Entry

EBX = Buffer ID


Exit

Carry clear  buffer released Carry set  bad ID


Uses

Flags


VDMAD_Request_Buffer
────────────────────────────────────────────────────────────────────────────


Description

This service reserves the DMA buffer for a DMA transfer.


Entry

ESI = linear address of actual DMA region ECX = # of bytes in DMA region


Exit

Carry clear  EBX = buffer ID  EDX = the physical address of the buffer Carry
set  AL = 5 (DMA_Buffer_Too_Small), region request is  too large for buffer
= 6 (DMA_Buffer_In_Use), buffer already in use


Uses

EAX, EBX, ESI, Flags


VDMAD_Reserve_Buffer_Space
────────────────────────────────────────────────────────────────────────────


Description

This service allows other devices that are going to handle DMA to make sure
that VDMAD allocates a buffer large enough for any transfers that they might
require. It also allows a device to specify a maximum physical address that
would be valid for the device's DMA requests (such as 1Mb for an XT.) During
the Device_Init phase of initialization, VDMAD will allocate the DMA buffer
using all of the contraints specified by other devices.i.e. the buffer will
be at least as big as the largest size specified by the calls to this
service, and it will be allocate below the lowest maximum physical addresses
specified.

This service is only available during Sys_Critical_Init.


Entry

EAX = # of pages requested ECX = maximum physical address that can be
included in a  DMA transfer; 0, if no limit.


Exit

None


Uses

Flags


VDMAD_Scatter_Lock
────────────────────────────────────────────────────────────────────────────


Description

This service attempts to lock all pages mapped to a DMA region and return
the actual physical addresses of the pages.


Entry

EBX = VM Handle AL = 0, if the DDS table should be filled with physical
addresses and sizes of the physical regions that  make up the DMA region AL
= 1, if the DDS table should be filled with the actual  page table entries
AL = 3, if the DDS table should be filled with the actual  page table
entries and not present pages should not  be locked EDI -> extended DDS (DMA
Descriptor Structure)


Exit

Carry clear  Z-flag set  whole region was locked successfully  Z-flag clear
partial region locked Carry set  nothing locked  EDX = # of table entries
needed to describe whole region DDS_size = # of bytes locked DDS table has
been updated if request was for page table copy (AL=1 OR 3), then  ESI =
offset into first page for start of the region


Uses

EDX, ESI, Flags


VDMAD_Scatter_Unlock
────────────────────────────────────────────────────────────────────────────


Description

This service attempts to unlock all pages locked by a previous call to
VDMAD_Scatter_Lock


Entry

EBX = VM Handle AL = 0, if the DDS table should be filled with physical
addresses and sizes of the physical regions that  make up the DMA region AL
= 1, if the DDS table should be filled with the actual  page table entries
AL = 3, if the DDS table should be filled with the actual  page table
entries and not present pages should not  be locked EDI -> extended DDS (DMA
Descriptor Structure)  (The table at the end of the DDS is not required, so
it is not necessary to maintain the table for this  unlock call.)


Exit

Carry clear  Lock counts have been decremented. If no other VxD's  had pages
locked, then the pages have been unlocked. Carry set  The memory was not
locked.


Uses

Flags


VDMAD_Set_EISA_Adr_Mode
────────────────────────────────────────────────────────────────────────────


Description

Set EISA extended mode


Entry

EAX = Channel # (0..7) or  DMA Handle CL = 0 - 8-bit I/O, with count in
bytes CL = 1 - 16-bit I/O, with count in words and adr shifted CL = 2 -
32-bit I/O, with count in bytes CL = 3 - 16-bit I/O, with count in bytes


Exit

None


Uses

Flags


VDMAD_Set_Phys_State
────────────────────────────────────────────────────────────────────────────


Description

This service programs the DMA controller state for a channel. All that it
needs to know is the desired mode. The location and size of the buffer is
taken from the information passed to the service VDMAD_Set_Region_Info which
must be called previously.


Entry

EAX = DMA handle EBX = VM handle DL = mode DH = extended mode


Exit

None


Uses

Flags


VDMAD_Set_Region_Info
────────────────────────────────────────────────────────────────────────────


Description

Set information about the current region assigned to a DMA handle. This
service must be called before calling VDMAD_Set_Phys_State.


Entry

EAX = DMA handle BL = buffer id BH = pages locked (0 = FALSE, else TRUE) ESI
= region linear ECX = size in bytes EDX = physical address for transfer


Exit

None


Uses

Flags


VDMAD_Set_Virt_State
────────────────────────────────────────────────────────────────────────────


Description

Modify the virtual state of a DMA channel. This is service is used when a
channel owner wants to change the virtual state of a channel from how the VM
programmed it.This might be used to split a DMA request into smaller pieces,
etc.


Entry

EAX = DMA handle EBX = VM handle If translation is enabled  ESI = high
linear address of the user's DMA region  (high linear is used so that the
DMA can proceed  even if a different VM is actually running at the  time of
the transfer) Else  ESI = physical byte address programmed (shifted left 1,
for word ports) ECX = count in bytes DL= mode (same as 8042 mode byte with
channel # removed  and DMA_masked & DMA_requested set as  appropriate:
DMA_masked channel masked and not ready  for a transfer  DMA_requested
software request flag set) DH= extended mode (ignored on non-PS2 machines
that don't  have extended DMA capabilities)


Exit

None


Uses

Flags


VDMAD_Unlock_DMA_Region
────────────────────────────────────────────────────────────────────────────


Description

This service unlocks the DMA region previously locked to a channel. It is
called after a DMA transfer is complete and the channel has been masked. So
that the controller will not attempt any further transfers to the programmed
address.


Entry

ESI = linear address of actual DMA region ECX = # of bytes in DMA region


Exit

Carry clear    memory unlocked Carry set    error


Uses

Flags


VDMAD_UnMask_Channel
────────────────────────────────────────────────────────────────────────────


Description

This service physically unmasks a channel so that DMA transfers can proceed.



Entry

EAX = DMA handle EBX = VM Handle


Exit

None


Uses

Flags


VDMAD_Virtualize_Channel
────────────────────────────────────────────────────────────────────────────


Description

This service allows another VxD to claim ownership of a standard DMA
channel. The new owner registers a callback routine that will be called
whenever the virtual state of the channel is changed as a result of I/O done
in a VM. In some cases a device doesn't want to allow a VM to perform DMA to
a channel at all (they will handle programming based on a private API, etc.
instead of virtualized hardware I/O), so it is possible to pass a 0 to
specify a null callback routine. VDMAD will continue to trap the I/O for the
channel, but won't ever change the physical state of the channel as a result
of any VM I/O.


Entry

EAX is Channel # ESI is I/O Callback procedure (0 = none)


Exit

Carry set if channel is already owned ELSE  EAX is DMA handle


Uses

Flags


Callback

ENTRY   EAX = DMA handle   EBX = VM handle   Proc can modify EAX, EBX, ECX,
EDX, ESI, EDI, and flags  EXIT   None






Chapter 42  Virtual DOSNET Device Services
────────────────────────────────────────────────────────────────────────────

The DOSNET device manages the network drives. To do so, it has a service
(DOSNET_Do_PSP_Adjust) that enables it to indicate that the network server
uses PSP addresses to identify tasks uniquely. Therefore, each VM's address
space should start at a unique address. It has another service
(DOSNET_Send_FILESYSCHANGE) that enables the Shell to determine whether or
not a particular drive is redirected in the System VM. These services are
required when network software is loaded with Windows in 386 enhanced mode.



DOSNET_Get_Version
────────────────────────────────────────────────────────────────────────────


Description

This service returns the DOSNET device version.


Entry

None


Exit

EAX = Version, Major in AH, Minor in AL Carry clear


Uses

Flags, EAX


DOSNET_Do_PSP_Adjust
────────────────────────────────────────────────────────────────────────────


Description

This service enables the DOSMGR device to ask whether or not it should
perform adjustments to try to give each VM in the system a different
starting MS-DOS PSP address. This needs to be done on networks (such as
MSNET) that use the PSP address as part of an ID to identify uniquely
different processes talking to the server (this effects the behavior of DOS
SHARE on the server end). On a network that uses MS-DOS PSP addresses as
part of an ID, enhanced Windows can cause the ID to be non-unique since
there are now multiple VMs that can all have an application in them that has
the same PSP address. If the PSP adjust is enabled by this service, the
DOSMGR device causes each VM to start at a different paragraph address (VMID
is the basis of the adjustment value) and thus have a different PSP address.
This has the cost of wasting some memory and the benefit of making the PSP
addresses different in all the VMs.

For a network that does not use MS-DOS PSP addresses for anything, or for
one that is enhanced-Windows aware and uses the Get_VMID INT 2F service of
enhanced Windows to deal with this problem, a return of Carry SET, EAX  0 is
appropriate.

Notice that the uniqueness of PSPs is not guaranteed by this. It will deal
with the case on most configurations, but on some it will not. The only
absolutely correct solution is to make the network software enhanced-Windows
aware and work the VMID into the network ID, in addition to the PSP address,
by using the Get_VMID INT 2F service of enhanced Windows.


Entry

None


Exit

Carry Set  Do not do PSP adjustment   EAX == 0 if SYSTEM.INI override of
this is allowed  EAX  0 if SYSTEM.INI override of this is not allowed Carry
Clear  DO PSP adjustment  EAX == 0 if SYSTEM.INI override of this is allowed
EAX 0 if SYSTEM.INI override of this is not allowed

Notice that the behavior of DOSMGR, if the DOSNET device is not loaded, is a
return of carry Set, EAX = 0.


Uses

Flags, EAX


DOSNET_Send_FILESYSCHANGE
────────────────────────────────────────────────────────────────────────────


Description

It is incorrect to send the WM_FILESYSCHANGE message to the Windows Shell on
drives about which Windows knows nothing. This routine tells the caller
whether this is a "local to this VM" drive or not. The message enables
Windows applications to be told when a change is made to the file volume
name space so that they can update portions of their display that may be
showing that part of the file volume.

Sending a WM_FILESYSCHANGE message on a drive incorrectly can result in all
sorts of misbehavior. If the indicated drive letter is invalid in the System
VM, the Windows application may get an error that it is not expecting. If
the indicated drive letter actually maps a different file system volume in
the System VM, all sorts of other unexpected errors may occur.

Notice that this is a DOSNET device service and cannot be implemented
directly in another VxD. Another VxD that also wishes to effect the
WM_FILESYSCHANGE behavior must hook this service. Notice also that the
DOSNET device does not install if there is no REDIR, so a VxD that wants to
hook this service must ship with a modified DOSNET device that always loads
(i.e., with the real-mode init code removed).


Entry

EAX (AL) = Drive number (0 = A) EBX is VM Handle of VM involved


Exit

Carry Set  WM_FILESYSCHANGE should not be sent on this drive Carry Clear
WM_FILESYSCHANGE should be sent on this drive


Uses

Flags






Appendix A  Appendix A Terms and Acronyms
────────────────────────────────────────────────────────────────────────────

The following list explains the terms and acronyms that are found in the
Microsoft Windows Device Development Kit for Windows 3.0.

Banding
The process of dividing a display surface such as a page into smaller
horizontal rectangles, composing those individual bands within memory, and
then sending the output to the printer one band at a time.

Clipping
The process of removing any portion of a graphic image that extends beyond a
specified boundary.

Control Block
A per Virtual Machine data structure in which Virtual Devices and the
Virtual Machine Manager can maintain the VM's state information. The control
block shares the flat segment with the VMM and VxDs. It is pointed to by the
VM Handle.

Control Panel
A Windows application that lets you change system settings, including
printer assignments and system characteristics.

Descriptor Table
An array of segment descriptors. There are two kinds of descriptor tables:
the unique Global Descriptor Table, and the per-VM Local Descriptor Table.

Device Driver
The dynamic-link library that provides the hardware-dependent, low-level
interface between Windows GDI functions and the graphics output device.

Device Independent Bitmap (DIB)
A bitmap format that can be interpreted and converted by a device driver
into its own specific format. It is called "device independent" because any
driver capable of using DIBs can display (or otherwise use) the DIB to the
best of its ability.

Direct Memory Access (DMA)
An accelerated memory access technique that enables peripheral devices to
take control of the bus. See also Virtual DMA Services (VDS).

DOS Protected Mode Interface (DPMI)
The industry standard for non-Windows applications using protected mode. It
is available from Intel at 1-800-548-4725.

Dynamic Data Exchange (DDE)
A protocol that cooperating programs can use to exchange data without user
intervention.

Dynamic-Link Library (DLL)
An executable module that contains functions available to applications and
that is linked at run time, rather than at link time.

Enhanced Windows
Windows 3.0 when running in 386 enhanced mode.

Escape
A device-dependent operation that is not supported by the device-independent
GDI module. The entry point in the device driver is called Control(); in GDI
(i.e., to the application), it is called Escape().

Extended Memory Specification (XMS)
The XMS driver provides an API for managing extended memory. Windows uses
XMS to load its code and data into extended memory.

Flat Model
A memory organization in which there is only one segment.

Font Resource
A group of individual fonts that have various combinations of heights,
widths, and pitches.

GDI Library
A set of supporting functions for device drivers. These utilities include
versions of output functions such as BitBlt and StrBlt, a Transpose function
for banding devices, and priority queue functions for daisywheel printers.

Global Descriptor Table (GDT)
See Descriptor Table.

Graphics Device Interface (GDI)
A device-independent, high-level graphics manager. GDI provides the
interface that feeds graphics commands from Windows application programs to
the device driver.

IOPM
I/O Permission Map

Local Descriptor Table (LDT)
See Descriptor Table.

Metafile
A collection of GDI function calls stored in a binary coded form and used to
transfer device-independent pictures between programs.

Microsoft Macro Assembler (MASM)
An assembly language compiler.

Non-Windows Application
A program that does not make use of the Windows environment. Instead, it
calls MS-DOS and the BIOS, and accesses the hardware directly.

Page
A 4K block of contiguous memory locations.

Paging
A memory-management technique used by enhanced Windows to support virtual
memory. It simulates a large, unsegmented address space by using a small,
fragmented address space and some disk storage. Paging accomplishes this by
keeping the available memory space partly in memory and partly on disk.

Palette
The range of colors that the video adapter can display and manage.

Pixel
The smallest element of a physical display surface that can be independently
assigned color or intensity.

Pixel Array
A matrix of pixels that defines the color for a region on an actual display.
There is exactly one pixel definition for each addressable picture element
of a raster display covered by the pixel array.

Primitive
A basic graphic function to be performed.

Print Manager
The Windows utility that prints files without suspending the operation of
other programs. It also enables you to change the priority of print jobs or
to cancel them.

Printer Command Language (PCL)
The language used by Hewlett-Packard (R) Laserjet (R) and compatible
printers.

Privilege Rings
The protection value applied to segments and segment selectors. Enhanced
Windows uses the most privileged level (ring 0) for the VMM and VxDs. Code
and data for applications running in virtual-86 mode use the least
privileged level (ring 3). Protected-mode applications run in rings 1, 2, or
3.

Protected Mode (PM)
A mode of the 80386 and 80486 processors that provides, among other
capabilities, arbitrary mapping of segment register values to physical
memory. See Appendix B, "Understanding Modes," for a detailed description.
Applications that want to use protected mode must conform to the DPMI
specification.

Raster Device
A device that uses a matrix of pixels covering the entire screen or page
area (display or printed surface) to draw graphics. Pixels (points) are
turned on and off, bit-by-bit.

Real Mode
An execution mode of an Intel processor that functions as an 8086; also
called "real-address mode." The 80286, 80386, and 80486 processors run in
this mode when started up. See also Appendix B, "Understanding Modes."

Red, Green, Blue (RGB)
Values from a color table. This color table is used in mapping from a color
index to corresponding color values.

Resolution
The number of visibly distinct dots that can be displayed in a given area of
the screen. Typical resolution is 100 dots per inch.

Scaling
Coordinate scaling transforms points from one level to another. GDI scales
coordinates from NDC space to values appropriate for your graphics device.

SETUP.INF
A file containing all the information needed to set up a device driver for
Windows. See also Appendix C, "Creating Distribution Disks for Drivers."

SYSTEM.INI
A file in the Windows directory that holds the Windows configuration
information that is used when starting up Windows. It differs from WIN.INI,
which is mainly for storing user preferences, by containing hardware and
software descriptions.

System VM
The first Virtual Machine (VM) under enhanced Windows. The VM in which
Windows runs.

Task
A program that is running or waiting to run.

Task Switch
A transfer of execution between tasks (i.e., a context switch). Unlike
procedure calls, which saves only the contents of the general registers, a
task switch saves most of the processor state. For example, the registers
used for address translation are reloaded, so that each task can have a
different logical-to-physical address mapping.

TSRs
Terminate-and-Stay Resident applications.

VCD
Virtual Comm Device

VDD
Virtual Display Device

VDMAD
Virtual DMA Device

VFD
Virtual Floppy Drive Device

VHD
Virtual Hard Disk Device

VKD
Virtual Keyboard Device

VMD
Virtual Mouse Device

VPD
Virtual Printer Device

VPICD
Virtual Programmable Interrupt Controller Device

Vector Device
A device that draws graphics with lines. Beginning and ending points are set
and a line is drawn between them.

Virtual 8086 mode (V86)
A mode of the 80386 processor by which the 80386 emulates the function of
the 8086 processor. In this mode, each segment has a linear address limit of
64K, and the applications can address a total of 1M + 64K - 16 bytes.
Software running in V86 mode, however, can use the 80386's 32-bit registers.

Virtual DMA Device (VDMAD)
The VDMAD is the Windows virtual device that supports standard DMA devices
on ISA machines as well as the VDS API for non-ISA devices.

Virtual DMA Services (VDS)
The VDS specification describes how an MS-DOS DMA device driver can perform
DMA. VxD authors should use the services of the VDMAD rather than work
directly from this specification.

Virtual "x" Device (VxD)
The name of the device virtualized replaces the "x" in this name. There must
be a VxD for each piece of hardware that can have a different state in each
of the VMs. Any piece of hardware that does not have an associated VxD is
global. It must handle interleaved access from multiple VMs or have a global
piece of software (such as a DOS device driver or TSR) that serializes
access to the hardware. All the VxDs run in the same, flat-model, 32-bit
segment as the rest of the VMM. A VxD can also provide services that are not
directly associated with a piece of hardware (e.g., a piece of code that
replaces an MS-DOS or BIOS service).

Virtual Machine (VM)
The part of the enhanced Windows environment in which Windows and
non-Windows applications execute. A VM consists of all the resources
(physical and/or virtualized) available to the application, plus the state
of the application. The state of the application includes the memory, the
processor state, and the state of the VMM and VxDs as maintained in the VM
control block.

Virtual Machine Manager (VMM)
The core of enhanced Windows. It runs, along with all the VxDs, in one,
flat-model, 32-bit segment.

Virtual Memory
Memory that an application can access that is greater than the actual
physical memory present. To support this, enhanced Windows transparently
pages 4K memory blocks to and from physical memory on behalf of the
applications.

WDEB386
A Windows 3.0 debugger program that can be used in protected mode with
either an 80286, 80386, or 80486 processor.

Window
A rectangular region on a display screen in which the system displays the
contents of an application.

Windows Application
Any program that has been specifically designed to run under Microsoft
Windows.

WIN.INI
The Windows initialization file in which you maintain the system-wide
settings for user preferences. The SYSTEM.INI file contains the hardware and
software descriptions. WIN.INI is a text-based file that resides in the
Windows software directory.

XMS
See Extended Memory Specification.






Appendix B  Understanding Modes
────────────────────────────────────────────────────────────────────────────

Microsoft Windows 3.0 documentation uses the term "mode" in overlapping
ways. This appendix is provided to clarify the different uses.


B.1  Windows Modes

To provide the greatest features for the available hardware, Windows 3.0 can
run in three software modes: real, standard, or 386 enhanced. The following
table compares the memory models and required microprocessor for each of
these Windows modes.

╓┌────────────┌──────────┌──────────────┌────────────────────────────────────╖
Windows 3.0  Real Mode  Standard Mode  386 Enhanced Mode
────────────────────────────────────────────────────────────────────────────
Supported    Real Mode  Real Mode      Real Mode
 Memory                  Protected      Protected Mode (32-bit)
 Model                  Mode (16-bit)   V86 Mode
Windows 3.0  Real Mode  Standard Mode  386 Enhanced Mode
────────────────────────────────────────────────────────────────────────────
 Model                  Mode (16-bit)   V86 Mode

────────────────────────────────────────────────────────────────────────────

Required     8086
Hardware      80286      80286          80386
              80386      80386          80486
              80486      80486

────────────────────────────────────────────────────────────────────────────




B.2  Microprocessor Modes

As the Intel microprocessors evolved greater capabilities, they continued to
support the programs and operating systems of the earlier architectures. As
a result, the 80386 has no fewer than four modes. Each is compared below to
the earlier architectures.

The first is the familiar real mode, wherein the 80386 functions as a fast
8086/88-compatible processor with some bonus opcodes. Like the 80286, the
80386 always powers up in real mode and can, therefore, run any existing
8086 operating systems and software.

In protected mode, the 80386 can take on two different personalities. It can
execute a logical superset of the 80286 protected-mode instructions and run
16-bit programs. Or, while in its native protected mode, it can use 32-bit
instructions, registers, and stacks and can allow individual memory segments
as large as 4GB. The native protected mode also has an additional level of
address translation─supported in hardware by page tables─that allows much
greater flexibility in mapping the linear address onto physical memory. In
either protected mode, the 80386 translates selectors and offsets to linear
addresses using descriptor tables in much the same manner as the 80286.

The forth operating mode, virtual 86 mode (V86), provides another form of
8086 emulation. But now, instead of a single program running in a single
memory partition, the 80386 can create multiple partitions, each capable of
running a real-mode program. Each partition has its own address space, I/O
port space, and interrupt vector table. Enhanced Windows uses the V86-mode
partitions to create virtual machines, the fundamental components in its
virtual machine architecture. The architecture is described in Chapter 16,
"Overview of Windows in 386 Enhanced Mode."

The following table summarizes the four modes of the 80386 microprocessor:

╓┌───────────────────────────────────────┌───────────────────────────────────►
Mode                                    Description
────────────────────────────────────────────────────────────────────────────
Real Mode                               Functions as a very fast
                                        8086/88-compatible processor.

Protected Mode (16-bit)                 Functions in protected mode as an
                                        enhanced 286 processor.

Protected Mode (32-bit, native mode)    Functions in protected mode using ful
                                        32-bit instructions, registers, and
                                        stacks.

Virtual 86 Mode                         Runs multiple, protected, virtual 808
Mode                                    Description
────────────────────────────────────────────────────────────────────────────
Virtual 86 Mode                         Runs multiple, protected, virtual 808
                                        machines, each with its own 1MB of
                                        memory space.

────────────────────────────────────────────────────────────────────────────








Appendix C  Creating Distribution Disks for Drivers
────────────────────────────────────────────────────────────────────────────

Once you have developed your device driver and/or virtual device, you must
create a distribution disk for installing it into the user's Windows
environment.

In Windows 3.0, we have included two Windows utilities (i.e., Setup and
Control Panel) that make installing device drivers and virtual devices that
are not available in the retail product much easier for the user. These
utilities do this by using the data stored in the Information (.INF) file
that is provided in the Windows retail product.

This appendix provides information on the following topics:


  ■   How Setup and Control Panel use Information files

  ■   The various components of the Information file

  ■   How to create an Information file for installing your device driver
      and/or virtual device

  ■   Syntax and examples of the data in an Information file, given by
      device type

  ■   How to use Setup and Control Panel to test the installation of your
      device driver and/or virtual device

  ■   How to inform Microsoft about the availability of your device driver
      and/or virtual device




C.1  What is an Information File?

An Information (.INF) file provides the data required for Windows to install
drivers and virtual devices that are provided on a set of disks.

It contains the following type of information:


  ■   The location of the driver and virtual device files (e.g., disk)

  ■   The device description string to be presented to the end user

  ■   Which driver and/or virtual device or other dependent files to
      install, depending on the device. For example, which screen fonts to
      install for a particular display and the associated virtual device
      (when setting up on an 80386 machine)

  ■   System configuration data for various machine types


The Setup utility uses the data in the Information (.INF) file to install
the following types of configuration information, device drivers, and
virtual devices:


  ■   Displays and associated font files

  ■   Mice or other pointing devices

  ■   Networks

  ■   Keyboard types, subtypes, and layouts

  ■   386 enhanced mode virtual devices

  ■   Machine dependent configurations:

      Display

      Mouse or other pointing device

      Keyboard

      Comm

      Sound

      System

      Any required 386 enhanced mode virtual devices and switches


Control Panel uses the data in the Information (.INF) file to install
printer drivers and Windows raster (or screen) fonts.


C.2  Different Types of Information Files

The .INF file provided with Windows is named SETUP.INF and is stored in the
Windows ..\SYSTEM subdirectory. The SETUP.INF filename is unique and used by
Setup and Control Panel for standard Windows operations. This name is
reserved for Windows and should not be used on your distribution disk.

A second type of .INF file that is used by Setup and Control Panel is called
OEMSETUP.INF. This name is to be used specifically for installing additional
drivers on a distribution disk.

Setup and Control Panel look for this filename, OEMSETUP.INF, when
installing drivers and virtual devices that are not part of the retail
package. In general, the OEMSETUP.INF file you create will contain a subset
of the information provided in the SETUP.INF file.

You may want to print a copy of the SETUP.INF file to use as a reference for
the following sections. The file is in ASCII text format and can be printed
on most printers.

The steps for installing additional drivers are described in more detail
later in Section C.7, "Testing the Installation of Your Information File."


C.3  General Format and Syntax for Information Files

.INF files are broken up into sections, with each section generally
containing information pertaining to a specific set of Windows driver or
system files. Section names are always enclosed in square brackets ([]).

Sections are further broken up into lines, with each line generally
containing information about a single Windows system or driver file.

Finally, the lines within sections are further broken up into fields, with
each field within a line generally containing information specific to the
system or driver file listed on that line. Fields are always separated by
commas (,).

The following is a generalized syntax line and, then, a sample section from
the SETUP.INF file:

Profile_string = Disk:Filename,"Text_Description",Disk:Virtual_Device

  [pointing.device]
  nomouse   = 2:nomouse.drv,   "No mouse or pointing device",     3:*vmd
  ps2mouse  = 1:mouse.drv,     "Microsoft, or IBM PS/2",          3:*vmd
  msmouse1  = 2:msmouse1.drv,  "Mouse Systems (or VisiOn) COM1:", 3:*vmd
  msmouse2  = 2:msmouse2.drv,  "Mouse Systems (or VisiOn) COM2:", 3:*vmd
  lmouse    = 2:lmouse.drv,    "Logitech",                        3:*vmd
  kbdmouse  = 2:kbdmouse.drv,  "AT&T/Olivetti Keyboard Mouse",    3:*vmd
  hpmouse   = 2:hpmouse.drv,   "HP Mouse (HP-HIL)",               3:*vmd

In the above example, the section name is [pointing.device]. Each line in
the section contains information pertaining to the mouse driver listed in
the first field of the line.

The string on the lefthand side of the equal sign (=) in the first field is
a profile string. The profile string is the unique "handle" that Setup uses
to parse a line from the .INF file that matches the hardware for which Setup
is configuring Windows. For example, if Setup detects that a Logitech mouse
is connected to the computer for which it is configuring Windows, it will
look for the string "lmouse" in the [pointing.device] section of the .INF
file. Once found, Setup uses the information on that line to determine how
to configure Windows for the Logitech mouse. The profile string portion of
the first field is considered to be field zero or may be referred to as the
profile field. Profile strings are limited to 15 characters.

The second field of each line is a Text_Description string for the mouse
driver on that line. Description strings are limited to 48 characters.

The third field is the name of the Virtual_Device file required for that
mouse to operate correctly under the 386 enhanced mode of Windows. If the
first character of any virtual device name is an asterisk (*), then that
driver is part of the WIN386.EXE file. If the virtual device name does not
contain an asterisk, then that file is separate from WIN386.EXE. 386
enhanced mode VxD files generally have the file extension .386 to identify
then as 386 enhanced mode VxDs.


C.4  File Location Information

All the files that are listed in the .INF file have associated with them a
logical disk ID. The logical disk ID is a single character (1-9 A-Z).
Therefore, there are 1-9 + A-Z = 35 possible logical disk IDs. The logical
disk ID is added to the beginning of every filename listed in the .INF file
and separated from the filename by a colon (:) in the form x:filename where
x is the logical disk ID. The logical disk ID zero (0) is reserved for use
by Setup.

────────────────────────────────────────────────────────────────────────────
NOTE

You must use a unique filename. We also suggest that this name be different
from the names of files defined in SETUP.INF.
────────────────────────────────────────────────────────────────────────────

The logical disk ID is mapped to a physical disk in the [disks] section of
the .INF file. The following is a generalized syntax line and, then, a
sample of a .INF file's [disks] section that maps the logical to physical
disk location of the mouse drivers listed in the [pointing.device] section.


Disk_ID = Drive,"Text_Description"

  [disks]
  1 =.\,       "Microsoft Windows 3.0 Disk #1"
  2 =.\disk2,  "Microsoft Windows 3.0 Disk #2"

In the preceding [disks] section example, the profile string corresponds to
a logical disk ID. For every logical disk ID used in the .INF file, there
must be a line in the [disks] section that maps that logical disk ID to a
physical disk location.

The first field in the [disks] section is the physical location that
corresponds to the logical disk ID in the profile string. In the preceding
example, all the filenames that start with logical disk ID 1 will exist in
the physical root of the current drive (.\). All the filenames that start
with logical disk ID 2 will exist in the physical subdirectory \disk2 of the
current drive.

The second field is a Text_Description string used to identify the physical
disk location. This is used by Setup and Control Panel for disk prompting if
the physical disk locations for different logical disk IDs are on different
floppy disks, which is normal.


C.5  Creating .INF File Entries

The following .INF file entries are examples to follow when creating an
OEMSETUP.INF file for displays, mice or other pointing devices, networks,
keyboards, and machine-dependent configurations.

See Section C.6, "Creating .INF File Entries for Printer Drivers," for
information on how to create an OEMSETUP.INF file for printer devices.

See Section C.3, "General Format and Syntax for Information Files," and
Section C.4, "File Location Information," for more information about profile
strings, text descriptions, driver filenames, and disk location
descriptions. These fields are referenced throughout the following examples.


The colons, commas, and quotes shown in the following examples are required
as part of the format.


C.5.1  Display Device

The following is a generalized example of the syntax used for display
devices along with definitions of each of the terms presented.

[display] Profile_string = Disk:Filename,"Text_Description","Aspect_Ratio",
Disk:Grabber,Disk:Logo_Code,Disk:Virtual_Display_Device,
Disk:386_Grabber,[Disk:EGA_DOS_Driver],Disk:Logo_Data

Where:

Filename is the MS-DOS filename of the driver file.

Text_Description is a character string describing the device on that line.

Aspect_Ratio is information that is used to determine which raster (or
screen) fonts to install for use by the device. Any fonts found with
matching aspect values in the [sysfonts], [fixedfonts], [oemfonts], or
[fonts] sections of the .INF file will be installed in the [fonts] section
of the WIN.INI file.

Aspect_Ratio includes the following three values:


  ■   X and Y aspect ratio

  ■   X pixels-per-inch

  ■   Y pixels-per-inch


The three values are separated by commas and enclosed within quotation
marks. For example, for a VGA display, the values would be as follows:

"100,96,96"

Where:

100 is the X and Y aspect ratio (1-to-1).

96 is the X (horizontal) pixels-per-inch.

96 is the Y (vertical) pixels-per-inch.

See the description of the GDIINFO data structure in Chapter 2, "Display
Drivers," in the Microsoft Windows Device Driver Adaptation Guide for more
information on calculating these values.

Grabber is the MS-DOS filename of the grabber used when Windows is running
in real and standard mode.

Logo_Code is the MS-DOS filename of the logo display code that is used by
Setup to build the WIN.COM file.

Virtual_Display_Device is the MS-DOS filename of the VDD that Windows will
use in 386 enhanced mode.

386_Grabber is the MS-DOS filename of the grabber used when Windows is
running in 386 enhanced mode.

EGA_DOS_Driver is an optional field. It is the MS-DOS filename of an MS-DOS
installable device driver used to virtualize the write-only registers on EGA
display cards so that they can be read.

Logo_Data is the MS-DOS filename of the logo data that is used by Setup to
build the WIN.COM file.

The following is an example of what a .INF file for a specific EGA display
driver would look like.

  [data]
  defxlat = 437

  [disks]
  1 =. ,"OEM Display Driver Disk #1"

  [display]
  oemega = 1:ega.drv,"OEM EGA Color","133,96,72",
  1:egacolor.gr2,1:egalogo.lgo,1:vddega.386,
  1:ega.gr3,1:ega.sys,1:egalogo.rle

  [EGA.GR3]
  1:CGA40WOA.FON,1:CGA40850.FON
  1:CGA80WOA.FON,1:CGA80850.FON
  1:EGA40WOA.FON,1:EGA40850.FON
  1:EGA80WOA.FON,1:EGA80850.FON

  [codepages]
  ;       Xlat Table    OEM Font      description.
  863 = 2:xlat863.bin, 2:vga863.fon, "French Canadian (863)"
  861 = 2:xlat861.bin, 2:vga861.fon, "Icelandic (861)"
  865 = 2:xlat865.bin, 2:vga865.fon, "Norway & Denmark (865)"
  850 = 2:xlat850.bin, 2:vga850.fon, "International (850)"
  860 = 2:xlat860.bin, 2:vga860.fon, "Portuguese (860)"
  437 =              ,             , "Standard (437)"

  [sysfonts]
  1:egasys.fon,"EGA (640x350) resolution System Font","133,96,72"

  [fixedfonts]
  1:egafix.fon,"EGA (640x350) resolution Fixed System Font","133,96,72"

  [oemfonts]
  1:egaoem.fon,"EGA (640x350) resolution Terminal Font
(USA/Europe)","133,96,72",1

  [fonts]
  1:HELVB.FON,   "Helv 8,10,12,14,18,24 (EGA res)",    "133,96,72"
  1:COURB.FON,   "Courier 10,12,15 (EGA res)",         "133,96,72"
  1:TMSRB.FON,   "Tms Rmn 8,10,12,14,18,24 (EGA res)", "133,96,72"
  1:SYMBOLB.FON, "Symbol 8,10,12,14,18,24 (EGA res)",  "133,96,72"
  1:ROMAN.FON,   "Roman (All res)",  "CONTINUOUSSCALING"
  1:SCRIPT.FON,  "Script (All res)", "CONTINUOUSSCALING"
  1:MODERN.FON,  "Modern (All res)", "CONTINUOUSSCALING"

Because there are many display-dependent files, the .INF file for installing
display drivers is more complicated. For any given Windows display driver,
the following related files are also required by Windows 3.0.

╓┌───────────────────────────────┌───────────────────────────────────────────╖
File                            Needed by
────────────────────────────────────────────────────────────────────────────
Display driver                  All modes
Standard-mode grabber           Standard and real mode
Enhanced-mode grabber           Enhanced mode
Logo display code               All modes
Logo display data               All modes
File                            Needed by
────────────────────────────────────────────────────────────────────────────
Logo display data               All modes
System font                     All modes
Fixed font                      All modes
OEM/Terminal font               Standard and real mode
386 OEM/Terminal font           Enhanced mode
User fonts                      All modes


The profile string for the display driver must be unique from all the other
display profile strings, including those listed in the Microsoft Windows 3.0
SETUP.INF file.

Setup selects fonts by matching the aspect ratio of the display to that of
the font. Setup will, however, ignore aspect ratio information for
configurations using a code page other than 437. For these configurations,
Setup asks MS-DOS for code page information and selects an OEM/Terminal font
from the [codepages] section based on that information. Since only MS-DOS
version 3.30 and higher supports the call for code page information, Setup
may (depending on the MS-DOS version) rely on the defxlat profile string
(under the [data] section) to get code page information.

When preparing your distribution disks, you may choose one of the following
three methods for providing .FON font files:


  ■   Include the appropriate Windows .FON font files from the Windows 3.0
      retail product.

  ■   Refer in the [disks] section of your OEMSETUP.INF file to the Windows
      3.0 retail disk(s) containing these files.

  ■   Distribute your own .FON font files if your display requires fonts
      that are tuned for a particular aspect ratio and not supported by the
      Windows .FON font files.


See the "Grant of License to Use" section, in the Microsoft License
Agreement that is printed on the DDK disk envelope, for information on your
rights to distribute these font files should you choose to include them on
your distribution disk.


C.5.2  Mouse or Other Pointing Device

The following is a generalized example of the syntax used for mice or other
pointing devices along with definitions of each of the terms presented.

[pointing.device] Profile_string =
Disk:Filename,"Text_Description",Disk:Virtual_Mouse_Driver

Where:

Filename is the MS-DOS filename of the driver file.

Text_Description is a character string describing the device on that line.

Virtual_Mouse_Driver is the MS-DOS filename of the associated virtual device
to be installed for running Windows in 386 enhanced mode.

The following is an example of what a .INF file for a specific OEM mouse
driver would look like.

  [disks]
  1 =. ,"OEM Mouse driver disk #1"

  [pointing.device]
  oemmouse = 1:oemmouse.drv,"OEM Mouse driver",1:oemvmd.386


C.5.3  Network Device

The following is a generalized example of the syntax used for network
devices along with definitions of each of the terms presented.

Notice that all the fields in brackets ([ ]) are optional. All other fields
are required. The quotes, commas, and colons are required as well.

Profile_string = Disk:Filename,"Text_Description", [Disk:Help_File],
[Disk:Optional_File],[WIN.INI_ Mods], [SYSTEM.INI_
Mods],[Disk:Virtual_Net_Device ...]

Where:

Filename is the MS-DOS filename of the driver file.

Text_Description is a character string describing the device on that line.

Help_File is the MS-DOS filename of an associated Help file. Setup copies
this file to the Windows installation directory. This file is optional.

Optional_File is the MS-DOS filename of an optional file, which may be
necessary for your network. Setup copies this file to the Windows
installation directory. This file is optional.

WIN.INI_Mods is the name of a section within the OEMSETUP.INF file that
describes modifications to be made to the user's WIN.INI file.

SYSTEM.INI_Mods is the name of a section within the OEMSETUP.INF file that
describes modifications to be made to the user's SYSTEM.INI file.

Virtual_Net_Device is the MS-DOS filename of an associated virtual device to
be installed for running Windows in 386 enhanced mode.

The following is an example of what a .INF file for a specific network
driver would look like.

  [disks]
  1 = . ,"Oem Network driver disk #1"

  [network]
  oem_net = 1:oem.drv,"OEM Network Driver",
1:oemnet.hlp,1:oem_app.exe,oem_winini,oem_sysini,
  1:oem1.386,1:oem2.386

  [oem_winini]
  Windows,load,oem_app.exe

  [oem_sysini]
  386enh,TimerCriticalSection,500

The oem_sysini and oem_winini sections describe modifications to be made in
the WIN.INI and SYSTEM.INI files. A .INI modification description section is
comprised of lines that contain three fields describing a new entry to be
made to the .INI file. The fields in each line are as follows:


  ■   Field 1 = .INI file section name

  ■   Field 2 = Profile string or descriptor to the lefthand side of the
      equal sign

  ■   Field 3 = Descriptor to the righthand side of the equal sign


In the preceding example, Setup would add the string "load=oem_app.exe" to
the [windows] section of WIN.INI. If a "load=" line already existed in
WIN.INI, then Setup would add "oem_app.exe" to the righthand side of the
"load=" line.

Setup would also add the string "TimerCriticalSection=500" to the [386enh]
section of SYSTEM.INI.


C.5.4  Keyboard Device

The following are generalized examples of the layout, type, and subtype
syntax used for keyboard devices along with definitions of each of the terms
presented.

The layout syntax is as follows:

Profile_string = Disk:Filename,"Text_Description"

The type and subtype syntax is as follows :

Type_Subtype_Profile_string = "Text_Description"

Where:

Filename is the MS-DOS filename of the driver file.

Text_Description is a character string describing the device on that line.

Type_Subtype_Profile_string is a combination of type/subtype and profile
string information. The type/subtype information is represented in the form
of "t##s##." The remaining characters represent the profile string
information.

The following is an example of what a .INF file for a specific keyboard
driver would look like.

  [data]
  defkeydll = ussrdll

  [disks]
  1 = . ,"Oem Keyboard disk #1"

  [keyboard.types]
  t3s9oem  = "Russian OEM Keyboard"

  [keyboard.tables]
  ussrdll = 1:kbdussr.dll,"Russian"

The keyboard type entry is unique because it involves only SYSTEM.INI
settings. No driver files are copied. The keyboard type and subtype entries
in SYSTEM.INI are used by the Windows 3.0 keyboard driver during boot time
to determine the keyboard type.

If the keyboard type is selected during a first time installation, Setup
will also select a keyboard layout (DLL or dynamic-link library). Setup
selects a keyboard DLL by looking at the data on the righthand side of the
equal sign of defkeydll in the [data] section of the .INF file. Using the
profile string obtained from this data, Setup selects a keyboard DLL from
the [keyboard.tables] section of the .INF file.


C.5.5  Machine-Dependent Configuration

Setup installs machine-dependent configuration information for the display,
keyboard, mouse, sound, comm, system, and 386 enhanced-mode BIOS devices,
and their related files, other virtual devices, and special SYSTEM.INI
switches.

The following is an example of how to define a machine-dependent
configuration in a .INF file by combining the information discussed in the
previous sections.

  [data]
  defxlat = 437
  deflang = rus

  [disks]
  1 =. ,"OEM Display Driver Disk #1"

  [display]
  oemega = 1:ega.drv,"OEM EGA Color","133,96,72",
  1:egacolor.gr2,1:egalogo.lgo,1:vddega.386,
  1:ega.gr3,1:ega.sys,1:egalogo.rle

  [EGA.GR3]
  1:CGA40WOA.FON,1:CGA40850.FON
  1:CGA80WOA.FON,1:CGA80850.FON
  1:EGA40WOA.FON,1:EGA40850.FON
  1:EGA80WOA.FON,1:EGA80850.FON

  [sysfonts]
  1:egasys.fon,"EGA (640x350) resolution System Font","133,96,72"

  [fixedfonts]
  1:egafix.fon,"EGA (640x350) resolution Fixed System Font","133,96,72"

  [oemfonts]
  1:egaoem.fon,"EGA (640x350) resolution Terminal Font
(USA/Europe)","133,96,72",1

  [codepages]
  ;       Xlat Table    OEM Font      description.
  863 = 2:xlat863.bin, 2:vga863.fon, "French Canadian (863)"
  861 = 2:xlat861.bin, 2:vga861.fon, "Icelandic (861)"
  865 = 2:xlat865.bin, 2:vga865.fon, "Norway & Denmark (865)"
  850 = 2:xlat850.bin, 2:vga850.fon, "International (850)"
  860 = 2:xlat860.bin, 2:vga860.fon, "Portuguese (860)"
  437 =              ,             , "Standard (437)"

  [fonts]
  1:HELVB.FON,   "Helv 8,10,12,14,18,24 (EGA res)",    "133,96,72"
  1:COURB.FON,   "Courier 10,12,15 (EGA res)",         "133,96,72"
  1:TMSRB.FON,   "Tms Rmn 8,10,12,14,18,24 (EGA res)", "133,96,72"
  1:SYMBOLB.FON, "Symbol 8,10,12,14,18,24 (EGA res)",  "133,96,72"
  1:ROMAN.FON,   "Roman (All res)",  "CONTINUOUSSCALING"
  1:SCRIPT.FON,  "Script (All res)", "CONTINUOUSSCALING"
  1:MODERN.FON,  "Modern (All res)", "CONTINUOUSSCALING"

  [pointing.device]
  oemmouse = 1:oemmouse.drv, "Mouse driver for OEM machine", 1:oemvmd.386

  [language]
  rus = 1:langussr.dll, "Russian"

  [keyboard.types]
  t4s8enha = "Super OEM 200 key Keyboard"

  [keyboard.drivers]
  oemkbd = 1:oemkbd.drv

  [system]
  oemsys  = 1:oemsys.drv
  oemsnd  = 1:oemsnd.drv
  oemcomm = 1:oemcom.drv

  [ebios]
  oemebios = 1:oemebios.386,x:*ebios

  [machine]

  "386 OEM Machine","78"
    oemsys    ;1
    oemkbd    ;2
    t4s8enha    ;3
    oemmouse    ;4
    oemega    ;5
    oemsnd    ;6
    oemcomm    ;7
    nohimemswitch   ;8
    oemebios    ;9
    "emmexclude=c700-c800"  ;10
    "device=",1:oemvxd.386"  ;11
        ;n

Setup may or may not use all the configuration information given in the
[machine] section. It does use the information provided to install the
system, keyboard layout, sound, comm, and BIOS drivers. However, only if
Setup is unable to detect the hardware in question, is the information for
the mouse, display, and keyboard type/subtype used.

In the preceding example, lines 10 and 11 under the [machine] section are
optional. These lines are provided for the installation of special virtual
devices or SYSTEM.INI [386enh] section switches. Up to six of these lines
may be added as needed.

Setup also uses code page information (obtained either by means of an MS-DOS
call, on MS-DOS versions 3.30 or later, or from the defxlat profile string
in the [data] section) to install a keyboard translation table. This
information is obtained from the .INF file in the [codepages] section.

Finally, Setup also selects a language driver based on the profile string
given on the righthand side of the equal sign of the deflang entry in the
[data] section of the .INF file.


C.6  Creating .INF File Entries for Printer Drivers

Control Panel does not require an OEMSETUP.INF file for installing a printer
driver. In Windows 3.0, Control Panel only uses the OEMSETUP.INF file to
install more than one set of raster (or screen) fonts and to install
dependent files (e.g., Help files or a font installer). Otherwise, it is not
required.

Control Panel scans the driver files for the driver description regardless
of whether an OEMSETUP.INF file is present or not. Therefore, it is
important for you to properly define your driver description in the
definition (.DEF) file.

The following subsections contain information on creating .INF entries for
OEMSETUP.INF and on the format of the driver's description string in the
.DEF file.


C.6.1  .INF File Entries

The following are generalized examples of the syntax used for printer
devices along with definitions of each of the terms presented. The colons,
commas, and quotes shown in the examples are required as part of the format.
The brackets ([ ]) represent optional fields, except in the case of section
names.

[io.device] Disk:Filename,"Text_Description",
"Aspect_Ratio"[,"Aspect_Ratio"...]

[io.dependent] Filename=Disk:File1[,Disk:File2...]

Where:

Filename is the MS-DOS filename of the driver file. It is used by Control
Panel to check on whether or not the driver has been installed previously
and to copy the driver from the distribution disk to the Windows ..\SYSTEM
directory. This driver name is also the first element on the righthand side
of the equal sign (=) in the entries under the [devices] and [PrinterPorts]
sections of the WIN.INI file. Filename in the [io.dependent] section must
match (case insensitive) the Filename element of the [io.device] section.

Text_Description is displayed in the printers listbox (without the quotation
marks) once the driver is installed. It is placed on the lefthand side of
the equal sign-in the entries under the [devices] and [PrinterPorts]
sections of the WIN.INI file. It should also match the Text_Description
string in the .DEF file of the driver (see the following section for more
information on the .DEF file). If there is a portion within the text
description that is enclosed in square brackets, it will be used as the
lefthand side of the equal sign (=) in the WIN.INI entries. The text in the
brackets would normally be used to provide a "generic" name for a driver
that may support many printer models (see the following example for the XYZ
Ink Jet printer).

Aspect_Ratio is information used to determine which raster (or screen) fonts
to install for use by the device. Any fonts found with matching aspect
values are installed in the [fonts] section of the WIN.INI file. Up to 5
different aspect ratio values may be listed. At least one must be listed. If
no raster font installation is desired, use "DEVICESPECIFIC" as the only
value.

Aspect_Ratio includes the following three values:


  ■   X and Y aspect ratio

  ■   X pixels-per-inch

  ■   Y pixels-per-inch


The three values are separated by commas and enclosed within quotation
marks. For example, for a VGA display, the values would be as follows:

"100,96,96"

Where:

100 is the X and Y aspect ratio (1-to-1).

96 is the X (horizontal) pixels-per-inch.

96 is the Y (vertical) pixels-per-inch.

See the description of the GDIINFO data structure in Chapter 2, "Display
Drivers," in the Microsoft Windows Device Driver Adaptation Guide for more
information on calculating these values.

File1 is the MS-DOS filename of the dependent file. This might include a
help (.HLP) file or a font installer that is associated with File1. You can
have more than one dependent file listed, but each must have a unique name.


See Section C.6.3, "Special Considerations," for information on how Control
Panel installs font files.

The following is an example of what a .INF file for a specific printer
driver would look like.

  [disk]
  1 = . ,"XYZ Printers Disk"

  [io.device]
  1:XYZLASER.DRV,"XYZ Laser Printer","DEVICESPECIFIC"
  1:XYZINK.DRV,"XYZ Ink Jet Printer [XYZ Series]",
  "100,120,120","100,96,96"

  [io.dependent]
  XYZLASER.DRV= 1:XYZLASER.HLP,1:SFXYZ.DLL

If the driver is not found in the current drive, Control Panel will prompt
the user for that disk.

The following is an example of a WIN.INI file after installation.

  [windows]
   |
  device=XYZ Series,XYZINK,LPT1:  ; Default printer, only one allowed at
  a time
   |

  [printerports]
  XYZ Series=XYZINK,LPT1:,15,45
  XYZ Laser Printer=XYZLASER,LPT2:,15,45

  [devices]
  XYZ Series=XYZINK,LPT1:
  XYZ Laser Printer=XYZLASER,LPT2:


C.6.2  Driver Description (.DEF) File

All drivers must provide a description string in the printer driver's .DEF
file. The following are a generalized example of the syntax used for printer
devices and a specific example along with definitions of each of the terms
presented.

DESCRIPTION 'DDRV Text_Description:Aspect_Ratio

DESCRIPTION 'DDRV PCL / HP LaserJet:100,300,300'

Where:

The character immediately following DDRV can be anything and is always
ignored.

DDRV must be capitalized.

Text_Description, up to but not including the colon, is placed on the
lefthand side of the equal sign for the entries in the [devices] and
[PrinterPorts] sections of the WIN.INI file. It is also the first element on
the righthand side of the "device=" line in the [windows] section. Because
the righthand side of the "device=" line is parsed using commas, if
Text_Description includes a comma, it will be terminated there. For example:


DESCRIPTION 'Printer 1, Printer2:100,300,300'

Printer 1 will be used as the lefthand and righthand sides in the above
mentioned sections. This will cause problems when installing with the
Control Panel. If you need to list more than one printer, we suggest you use
a delimiter other than a comma. For example:

DESCRIPTION 'Printer 1/Printer2:100,300,300'

Aspect_Ratio includes the following three values:


  ■   X and Y aspect ratio

  ■   X pixels-per-inch

  ■   Y pixels-per-inch


The three values are separated by commas and enclosed within quotation
marks. For example, for a VGA display, the values would be as follows:

"100,96,96"

Where:

100 is the X and Y aspect ratio (1-to-1).

96 is the X (horizontal) pixels-per-inch.

96 is the Y (vertical) pixels-per-inch.

See the description of the GDIINFO data structure in Chapter 2, "Display
Drivers," in the Microsoft Windows Device Driver Adaptation Guide for more
information on calculating these values.


C.6.3  Special Considerations

Control Panel references the SETUP.INF file in the Windows ..\SYSTEM
subdirectory for the raster font files. Control Panel will prompt the user
as necessary for the Microsoft Windows disk defined in the SETUP.INF file if
a font matching the Aspect_Ratio is available.

If dependent files exist, the OEMSETUP.INF file needs to exist in the same
directory as the driver file.


C.7  Testing the Installation of Your .INF File

Before testing, you need to create a .INF file according to the instructions
given in Section C.5, "Creating .INF File Entries," and Section C.6,
"Creating .INF File Entries for Printer Drivers." The .INF file and other
associated driver and virtual device files should then be copied to a disk
(or any other form on which you plan to distribute them).

Depending on the type of device you support, you will need to run either
Setup or Control Panel to test your .INF file.

Setup installs display, mouse or other pointing device, network, keyboard,
or machine-dependent drivers and virtual devices.

Printer drivers are installed by Control Panel.


C.7.1  Testing with Setup

You must run the Setup utility from MS-DOS when installing additional device
support (via an OEMSETUP.INF file). If you run Setup directly from Windows,
you can install only device drivers and virtual devices that are supplied
with Windows (i.e., defined in the SETUP.INF file).

During debugging, we suggest you use the debugging version of the Setup
utility, which is provided on the DDK disks. For more information on the
error messages that might appear, see Section C.9, "Error Messages When
Running Debug Setup."

Follow these steps to install additional drivers and virtual devices with
Setup:


  1.  Exit Windows.

      Do not use the DOS Prompt icon in the Main Group window. Using this
      icon will result in an incorrect setup.

  2.  Type setup and press ENTER.

      Setup lists your current Windows configuration.

  3.  Press the UP or DOWN ARROW key to select the setting you want to
      change and press ENTER.

      A list of choices is displayed.

  4.  Scroll to the bottom of the list using the DOWN ARROW key.

  5.  Select Other and press ENTER.

      Setup prompts you for the disk containing the device driver or virtual
      device.

  6.  Insert your distribution disk into drive A. Setup displays a list of
      the device drivers or virtual devices defined in the OEMSETUP.INF file
      on that disk. Then select the appropriate driver or virtual device and
      press ENTER.

      Or type the pathname of the device driver or virtual device and press
      ENTER.

  7.  Press ENTER to complete the process.


Be sure to install and test all the files described in your OEMSETUP.INF
file.

Setup automatically expands your files if you choose to use the COMPRESS.EXE
utility supplied in the SDK to compress your files. Notice, however, that
OEMSETUP.INF should not be compressed.


C.7.2  Testing with Control Panel

To test your OEMSETUP.INF file and the DESCRIPTION in your printer driver's
.DEF file, you need to run Control Panel.

Additional printer drivers are installed the same way you would install a
driver supplied by Windows. Instead of selecting a printer from the list (of
those defined in the SETUP.INF file) to install, you would select Unlisted
Printer from the bottom of the list.

Follow these steps to install an additional printer driver:


  1.  Run Control Panel and select the Printers Icon.

  2.  Choose the Add Printer button.

  3.  Select Unlisted Printer from the bottom of the printers list.

  4.  Choose the Install button.

  5.  Insert your distribution disk, if necessary.

  6.  Select the appropriate drive and directory path.

  7.  Select OK.

  8.  Select the appropriate driver filename from the Driver Files list.

  9.  Select OK.


At this point, Control Panel looks for OEMSETUP.INF. If found, Control Panel
will install all the raster font and associated files defined in the
[io.dependent] section (if applicable to the driver file that you selected).


If OEMSETUP.INF does not exist on this disk or in the specified directory,
Control Panel inspects the DESCRIPTION line within the driver and copies the
raster fonts that match exactly the values defined there.

Control Panel expands your files automatically if you choose to use the
COMPRESS.EXE utility supplied in the SDK to compress your files. Notice,
however, that the OEMSETUP.INF file should not be compressed.


C.8  Informing Microsoft About the Availability of Your Driver and/or
Virtual Device

Microsoft publishes the Microsoft Windows Software and Hardware Directory.
This publication is available to both developers and Windows users. The
directory contains information on the following:


  ■   Windows-compatible applications

  ■   Windows device drivers

  ■   Windows virtual devices

  ■   Windows-based consultants


Included in this package is a Microsoft Windows Directory Survey. If you
want to publish information in the directory about your device drivers
and/or virtual devices, please complete this survey and return it to the
address listed on the survey form. There is no fee for taking advantage of
this opportunity to tell Microsoft Windows 3.0 users all about the support
for your product.


C.9  Error Messages When Running Debug Setup

When using the debug version of Setup that is provided on the DDK disks, you
may see several error messages regarding possible Setup assertion failures.
The following list provides the error message numbers and a brief
description of each along with possible solutions:

╓┌─────────────────────────────────┌─────────────────────────────────────────►
Message                           Description
────────────────────────────────────────────────────────────────────────────
SUINF.C:68:                       Setup could not find a suitable (correct
                                  aspect ratio) system font for the display
                                  driver selected. Ensure that a matching
Message                           Description
────────────────────────────────────────────────────────────────────────────
                                  driver selected. Ensure that a matching
                                  aspect ratio font is available in the
                                  [sysfonts] section of the .INF file.

SUINF.C:77:                       Setup could not find a suitable (correct
                                  aspect ratio) OEM font for the display
                                  driver selected. Ensure that a matching
                                  aspect ratio font is available in the
                                  [oemfonts] section of the .INF file.

SUINF.C:80:                       Setup could not find a suitable (correct
                                  aspect ratio) OEM font for the display
                                  driver selected. Ensure that a matching
                                  aspect ratio font is available in the
                                  [oemfonts] section of the .INF file.

SUINF.C:84:                       Setup could not find a suitable (correct
                                  aspect ratio) fixed font for the display
                                  driver selected. Ensure that a matching
Message                           Description
────────────────────────────────────────────────────────────────────────────
                                  driver selected. Ensure that a matching
                                  aspect ratio font is available in the
                                  [fixedfonts] section of the .INF file.

SUINF.C:165:                      Setup detected a machine for which it
                                  could not find a machine entry in the
                                  [machine] section of the .INF file.

SUUTIL.C:451:                     Setup cannot find a disk entry in the
                                  [disks] section of SETUP.INF for a logical
                                  disk ID given in the .INF file. Check to
                                  see that all logical disk ID values have a
                                  corresponding entry in the [disks] section
                                  of the .INF file.








Appendix D  Windows INT 2FH API
────────────────────────────────────────────────────────────────────────────

Enhanced Windows 3.0 supports an application program interface (API)
designed to enable MS-DOS device drivers, TSR programs, and application
programs to take full advantage of the multitasking abilities of the
enhanced Windows environment.

Most application program writers will use the interface that releases the
current virtual machine's time-slice. This API allows enhanced Windows and
OS/2 to multitask non-Windows specific MS-DOS applications more efficiently.
The Release Time Slice API should be used by applications even if they are
not running under enhanced Windows. This allows OS/2 to detect idleness in
MS-DOS applications. OS/2 will recognize the enhanced Windows release
time-slice call but it does not support other enhanced Windows APIs.

The Microsoft 80286 DOS extender will issue the initialization and exit INT
2FH API calls so that real mode software can free extended memory through
XMS. The 286 DOS extender also supports the Int 31h service detection Int
2FH API call.

Other APIs are used by MS-DOS device drivers and TSRs that have enhanced
Windows specific requirements.


D.1  Call-In Interfaces

Call-in interfaces are APIs that real mode MS-DOS device drivers, TSRs, and
applications use to communicate with enhanced Windows. These include:


  ■   Get Windows version

  ■   Get virtual machine ID

  ■   Begin critical section

  ■   End critical section

  ■   Release time slice

  ■   Get device API entry point

  ■   Switch VMs and callback




D.1.1  Enhanced Windows Installation Check (AX=1600H)

This API call is valid under all versions of enhanced Windows. If a program
intends to use a enhanced Windows API, it must first make sure that the
enhanced Windows environment is running. To do this, issue:

  mov ax, 1600h
  int 2Fh
  test al, 7Fh
  jz Not_Running_Win386
  (Otherwise enhanced Windows is running)
  cmp al, 1
  je Running_Ver_2xx
  cmp al, -1
  je Running_Ver_2xx
  (Else al contains major version, AH contains minor)

If 0 or 80H is returned in AL, enhanced Windows is not running. Any other
value means that enhanced Windows is running. A value of 1 or -1 (0FFH)
indicates that the application is running under enhanced Windows version 2.0
or 3.0. Otherwise, AL will contain the major version number (3 or higher)
and AH will contain the minor version number. The table below summarizes the
possible return values:

╓┌──────────────┌──────────────────────────────────────────────────┌─────────►
Value in AL    Meaning
────────────────────────────────────────────────────────────────────────────
00H            Enhanced Windows 3.x or Windows/386 version 2.xx
               is not running

80H            Enhanced Windows 3.x or Windows/386 version 2.xx
               is not running

Value in AL    Meaning
────────────────────────────────────────────────────────────────────────────

01H            Windows/386 version 2.xx running

FFH            Windows/386 version 2.xx running

Anything else  AL = Major version number                          AH = Minor




D.1.2  Releasing Current Virtual Machine's Time-Slice (AX=1680h)

────────────────────────────────────────────────────────────────────────────
NOTE
This API should be used only by non-Windows specific applications. Windows
programs should yield their time by calling the WaitMessage function.
────────────────────────────────────────────────────────────────────────────

This API is used by programs to indicate that the program is idle (usually
waiting for the user to type something). By issuing this interrupt,
applications prevent enhanced Windows from wasting time running a program
that is essentially doing nothing. This allows other programs to use the
time.

Programs should always use this API even if they are not Windows-specific
applications and even if they are not currently running under Windows in 386
enhanced mode. This allows OS/2 to detect idleness even though it does not
support the complete enhanced Windows API. The only check you should make
before issuing the API call is to make sure that the INT 2FH interrupt
vector is not zero.

Sample code:

  mov ax, 352Fh
   int 21h  ; DOS get vector 2Fh
   mov ax, es  ; ES:BX = Vector
   or ax, bx  ; Q: Is it zero?
   jz Skip_Idle_Call ;    Y: Skip this
   mov ax, 1680h ;    N: Tell Win
   int 2Fh  ;       we're idle.
  Skip_Idle_Call:

If the API is supported, the INT 2FH will return with AL=0, otherwise it
will return with AL unchanged (80h). Usually application programs will not
be interested in the return value.

Note that when an application uses this API it will continue to run
occasionally so your program should re-issue the interrupt in the program's
idle loop. In other words, this API does NOT block your application until a
key is pressed.


D.1.3  Begin Critical Section (AX=1681h)

If an MS-DOS device driver or TSR needs to prevent a task-switch from
occurring, it should call this interface. When a virtual machine is in a
critical section, no other task will be allowed to run except to service
hardware interrupts. For this reason, the critical section should be freed
(using the end critical section API) as soon as possible.


D.1.4  End Critical Section (AX=1682h)

This API must be called to release ownership of the critical section that
was claimed using the Begin Critical Section API. Every call to Begin
Critical Section must be followed by a matching call to End Critical
Section.


D.1.5  Get Current Virtual Machine ID (AX=1683h)

This API returns with BX = Current virtual machine ID. The ID is unique for
each virtual machine. Although Windows currently runs in VM 1, your software
should not rely on this. Also, if a VM is destroyed, its ID may be reused by
another new virtual machine. Be sure to treat VM IDs as a word (not a byte).
An ID of 0 will never be returned.


D.1.6  Get Device API Entry Point (AX=1684h)

Some VxDs (enhanced Windows device drivers) provide a set of services that
application programs can access. For example, the Virtual Display Device
provides services that the Windows old application program uses to display
MS-DOS programs in a window. Any VxD can support an API for MS-DOS
applications. Your program must issue an INT 2FH with AX=1684h and BX =
Virtual device ID. The entry point address will be returned in ES:DI. Your
application must execute a FAR CALL to this address to call the virtual
device. If the value returned is 0:0 then the device does not support an
API, otherwise ES:DI is the address of the procedure to call. You should
either make sure your application is running on version 3.0 or zero ES and
DI before using this API.

  xor di, di ; * Only necessary if you have  *
  mov es, di ; * not checked for Win ver 3.0 *
  mov ax, 1684h
  mov bx, My_Device_ID
  int 2Fh
  mov ax, es
  or ax, di
  jz API_Is_Not_Supported
  (else API address in ES:DI)

The definition of a device API is specified by the virtual device driver.
Refer to individual virtual device documentation for details.


D.1.7  Switch VMs and CallBack (AX=1685h)

Some MS-DOS devices, such as networks, need to perform functions in a
specific virtual machine. These devices can use this interface to force the
appropriate virtual machine to be installed so that they can modify the VM's
data. Refer to Chapter 24, "Primary Scheduler Services," for information on
appropriate priority boosts.

Entry: AX = 1685h  BX = VM ID of virtual machine to switch to  CX = Flags
Bit 0 = 1 if wait until interrupts enabled  Bit 1 = 1 if wait until critical
section unowned  All other bits must be 0  DX:SI = Priority boost (DX=High
word, SI=Low word)  ES:DI = CS:IP of procedure to call

Exit:

  If carry set then
     AX = Error code
  else
     Event will be called or has been called already.

Error codes: 1 = Invalid VM ID  2 = Invalid priority boost  3 = Invalid
flags

Callback procedure: Must save all registers modified  Must IRET to caller
Priority will remain boosted until procedure irets


D.1.8  Detect Presence of INT 31H Services (AX=1686h)

If a program needs to detect the presence of the INT 31H protected mode API,
it can use this INT 2FH. Note that this particular API is also supported by
the Microsoft 80286 DOS extender for protected mode Windows. INT 31H
services are only supported for protected mode programs.


Entry

 AX = 1686h


Exit

If AX = 0 then    INT 31H services are available and can be called else (AX
0)    zINT 31H services are not available


D.2  Call Out Interfaces
────────────────────────────────────────────────────────────────────────────

Enhanced Windows will broadcast INT 2FH to real mode device drivers and TSRs
to inform them of various activities. These can be used to load enhanced
Windows installable devices, free extended memory, instance per-VM data
structures, and turn on or off various device services or features. For
example, SmartDrv can free extended memory for enhanced Windows to use when
the initialization call is made and then reclaim it when it receives the
termination call. MS-DOS devices such as networks can inform the enhanced
Windows loader to load a special protected mode installable device that
cooperates with the real mode network device driver.


D.2.1  Enhanced Windows and 286 DOS Extender Initialization (AX=1605h)

The enhanced Windows loader and the Microsoft 286 DOS extender will
broadcast an INT 2FH with the following parameters:

AX = 1605h ES:BX = 0:0 DS:SI = 0:0 CX = 0 DX = Flags    Bit 0 = 0 if
enhanced Windows initialization    1 if Microsoft 286 DOS extender
initialization    All other bits reserved and undefined. DI = Version #:
major version in upper byte, minor version in lower byte

Any MS-DOS device driver or TSR can hook Int 2FH and watch for this
particular broadcast. When this broadcast is received, the real mode
software can inform enhanced Windows or the 286 DOS extender that it should
not load by returning with CX  0. The TSR or device that fails the
initialization should print an error message so the user can take
appropriate steps to reconfigure the machine. Enhanced Windows and the
Microsoft DOS extender will not print an error message─they will only issue
the termination API call and return to MS-DOS.

If it is OK for enhanced Windows or the DOS extender to load, the real mode
software should not modify CX and may want to do one or more of the
following:


  ■   Release extended memory through the XMS interface.

  ■   Switch back to real mode (if currently in virtual 8086 mode) or set
      DS:SI to the Virtual 8086 mode enable/disable routine address.

  ■   Load an installable device (enhanced Windows only).

  ■   Instance private data structures (enhanced Windows only).


The DOS extender only pays attention to the value returned in CX. It will
not instance any data or load enhanced Windows installable device drivers.
The DOS extender only issues this call so that extended memory can be
released and the machine can be placed in real mode if it is currently in
virtual 8086 mode.

Instance data refers to data in a TSR or MS-DOS device driver that must have
a private copy in each VM. Normally, all TSRs and devices loaded before
enhanced Windows is run are considered global memory. That means that all of
the data is shared between virtual machines. However, there are some pieces
of data that actually should be maintained on a per-VM basis. For example,
the MS-DOS command line buffer needs to be instanced (this is done
automatically by enhanced Windows). However, TSRs such as the MS-DOS command
line editors will not function properly unless they identify the data that
needs to be instanced.

The first two options (release extended memory, or switch from V86 to real
mode) are up to the device to handle on its own. The last options require
returning a pointer to a list of structures to load. Your INT 2FH hook must
first chain to the next INT 2FH handler with all registers unmodified. When
the handler returns you must take the ES:BX value returned and place it in
the following data structure in the Next_Dev_Ptr field:

  Win386_Startup_Info_Struc STRUC
  SIS_Version   db 3, 0
  SIS_Next_Dev_Ptr   dd ?
  SIS_Virt_Dev_File_Ptr  dd 0
  SIS_Reference_Data  dd ?
  SIS_Instance_Data_Ptr  dd 0
  Win386_Startup_Info_Struc ENDS

Your software must point ES:BX at this structure and return. This allows
multiple enhanced Windows installable devices to be loaded through a single
INT 2FH call.

The SIS_Version field is used by enhanced Windows to determine the size of
the structure. This field should always contain 3, 0 to indicate that it is
version 3.0.

The SIS_Next_Dev_Ptr points to the next structure in the list. This field
must be filled in with the returned ES:BX after your software chains to the
next INT 2FH handler.

SIS_Virt_Dev_File_Ptr is a pointer to an ASCIIZ string that contains the
name of a enhanced Windows virtual device file. MS-DOS devices such as
networks use this to force a special enhanced Windows protected mode virtual
device to be loaded. If this field is zero, then no device will be loaded.

The SIS_Reference_Data is only used when the SIS_Virt_Dev_File_Ptr is
non-zero. This DWORD will be passed to the virtual device when it is
initialized. The DWORD can contain any value. Often it contains a pointer to
some device specific data structure.

The SIS_Instance_Data_Ptr field points to a list of data to be instanced. If
the field is zero, then no data will be instanced. Each entry in the list
has the following structure:

  Instance_Item_Struc STRUC
   IIS_Ptr   dd ?
   IIS_Size  dw ?
  Instance_Item_Struc ENDS

The list is terminated with a zero DWORD.

Your handler must preserve all registers except the values returned in ES,
BX, and CX. It must also preserve DS and SI unless it explicitly changes
them to return the address of the virtual 8086 mode enable/disable routine.
Remember, any device that returns with CX  0 will force enhanced Windows or
the 286 DOS extender to abort. If the load is aborted, the termination INT
2FH will be issued immediately.

Enhanced Windows supports loading with a virtual mode program such as an EMM
"LIMulator" running provided that the program supports a virtual 8086 mode
enable/disable callback routine. The address of the routine must be returned
in DS:SI. If your TSR or device driver sets this return parameter, it should
first check to make sure that DS and SI are both zero. If they are non-zero,
then fail the initialization by setting CX=non-zero. Notice that the
Microsoft 286 DOS extender will not call this routine. Therefore, you must
either set the processor into real mode during the initialization INT 2FH or
set CX=non-zero to abort the load.

The virtual mode enable/disable callback will be called with AX=0 to disable
V86 mode (return to real mode) and AX=1 to re-enable V86 mode. Just before
attempting to enter protected mode enhanced Windows will disable V86 mode
after every VxD has been loaded. It will call the enable/disable routine
with AX=0 and with interrupts disabled. Do not enable interrupts in your
routine unless the routine will return with Carry set to indicate failure.
After enhanced Windows exits, it will call the enable/disable routine in
real mode with AX=1 and with interrupts disabled to set the machine back
into V86 mode.

The enable/disable routine will be called with a FAR return frame. It must
return with the carry flag clear to indicate success or Carry set to
indicate an error. If an error is returned from the disable call, then
enhanced Windows will abort. The error return from the enable V86 call will
be ignored and the machine will be left in real mode. It is the
responsibility of the enable/disable routine to print an error message.


D.2.2  Enhanced Windows and 286 DOS Extender Exit (AX=1606h)

When enhanced Windows or the 286 DOS extender terminates, it will broadcast
an INT 2FH with the following parameters:

AX = 1606H. DX = Flags Bit 0 = 0 if enhanced Windows exit 1 if Microsoft 286
DOS extender exit All other bits reserved and undefined.

This call will be issued in real mode. It allows devices and TSRs to undo
anything they did when enhanced Windows or the DOS extender initialized. For
example, a device like SmartDrv may re-allocate extended memory that it
released during initialization.

If the initialization broadcast fails (returns with CX  0) then this
broadcast will be issued immediately.


D.2.3  Device Call Out API (AX=1607h)

This API is, in reality, more of a convention than an API. It specifies a
standard mechanism for enhanced Windows virtual devices to talk to MS-DOS
device drivers and TSRs.

Some devices need to ask real-mode MS-DOS software for information. For
example, the Virtual NetBIOS mapper VxD will issue an INT 2FH to determine
if a network supports an extended NetBIOS API. The standard device call out
will have AX=1607H and BX=Device ID. As with the device API entry point
call-in interface, the details of the interface are specified by the device
that issues the interrupt.

This interrupt may be issued at any time, either in real mode or after
enhanced Windows has begun execution.


D.2.4  Enhanced Windows Initialization Complete (AX=1608h)

This API call is made by enhanced Windows after all the installable devices
have initialized. At this point, it is still possible to identify instance
data and perform other functions that are restricted to enhanced Windows
initialization time. The enhanced Windows device initialization phase is
complete, so it is possible to call enhanced Windows device API entry
points.

────────────────────────────────────────────────────────────────────────────
WARNING

Real mode software such as a TSR or DOS device driver may be called after
the enhanced Windows initialization call and before this API call is made.
It is the responsibility of the real mode software to detect and properly
handle this situation.
────────────────────────────────────────────────────────────────────────────


D.2.5  Enhanced Windows Begin Exit (AX=1609H)

This API call is issued at the beginning of a normal Enhanced Windows exit
sequence. It is sent at the start of the Sys_VM_Terminate device control
call phase. All VxDs still exist so calls to device API entry points are
still valid.

────────────────────────────────────────────────────────────────────────────
WARNING

This call will not be made in the event of a fatal system crash. Also, real
mode code may be executed after this API call has been made and before
enhanced Windows has returned to real mode. It is the responsibility of the
real mode software to detect and properly handle these situations.
────────────────────────────────────────────────────────────────────────────


D.3  Windows/386 Version 2.xx API Compatibility
────────────────────────────────────────────────────────────────────────────

The release of Windows/386 (version 2.xx) had a limited Application Program
Interface that was defined to help support real mode MS-DOS device drivers
such as networks. The 2.xx API allows MS-DOS programs to:


  ■   Determine if Windows/386 or enhanced Windows (version 3.0) is running

  ■   Get the ID of the current Virtual Machine

  ■   Enter and leave a global critical section


The APIs used under version 2.xx are fairly complex and inflexible. We
suggest that, unless your application or device driver absolutely needs to
run under version 2.xx, you ignore all version 2.xx APIs and use the 3.0
APIs instead.


D.3.1  Installation Check

To test for Windows/386 version 2.xx, you should issue an Int 2fh with
AX=1600h. Refer to Windows Installation Check for complete documentation for
this API call.


D.3.2  Determining the Current Virtual Machine (Get VM ID)

Once the software has determined that it is running under Windows/386
version 2.xx, it must make another call to get the API procedure address. To
do this, issue:

  mov ax, 1602h
   int 2fh
   (ES:DI -> Windows/386 API procedure)

The API procedure is the same address for every virtual machine, so you will
need to issue this call only once (although you can issue it as often as you
want).

To get the ID of the current virtual machine jump to the Windows/386 API
procedure with AX = 0 and ES:DI - the address to return to.

Sample code:

  mov di, cs
   mov es, di
   mov di, OFFSET Win386_AIP_Return
   xor ax, ax   ; AX = 0
   jmp [Win386_API_Proc]
  Win386_API_Return:
   (Now BX contains the current VM ID)

Note that you must place the return address in ES:DI and JUMP to the API
procedure. When enhanced Windows returns control to your program it will
return to ES:DI.

This interface is supported under version 3.0 only for compatibility
reasons. New MS-DOS devices or applications should use the version 3.0
interface.


D.3.3  Critical Section Handling

Windows/386 version 2.xx does not support API calls to enter and leave a
critical section. However, by incrementing and decrementing a special DOS
critical section counter called the InDOS flag, you can force the current
virtual machine into a critical section. Incrementing InDOS is not
sufficient to enter a critical section in version 3.0. You will need to make
an API call first and then, if it fails, increment the InDOS flag.

To get the address of the InDOS flag, issue the following MS-DOS call
(documented in The MS-DOS Encyclopedia ):

  mov ah, 34h
   int 21h
   (ES:BX -> InDOS flag)

The InDOS flag is a byte within the MS-DOS kernel. The value in InDOS is
incremented when MS-DOS begins execution of an Interrupt 21H and decremented
when MS-DOS's processing of that function has completed. When you increment
the byte, current versions of enhanced Windows will not switch to another
virtual machine. Therefore, to enter a critical section, you need to
increment the byte and to leave a critical section you should decrement the
InDOS flag.

────────────────────────────────────────────────────────────────────────────
WARNING

You must make sure your code never decrements the InDOS flag through zero.
MS-DOS will set the InDOS flag to zero under some error conditions (for
example, the user types CTRL+C). Also, even if the InDOS flag is non-zero,
an Int 28H may cause the VM to be rescheduled.
────────────────────────────────────────────────────────────────────────────

For versions 3.xx and greater of Windows you will need to issue an INT 2FH
AX = 1681H to begin a critical section and AX = 1682H to end a critical
section. Note that if a program enters the critical section N times, it must
also issue the end critical section interrupt N times before the critical
section is actually released. Thus, nested begin/end critical section calls
are valid. Both of these APIs will zero the AL register to indicate that the
critical section API is supported. You should not increment and decrement
InDOS under versions of Windows that support these API calls.

Unlike the InDOS critical section method, an INT 28H will not reschedule the
current virtual machine. The only way a task switch will occur is by
completely releasing the critical section.

Since you need to call the Windows API or increment the InDOS flag you will
probably want to write two procedures similar to the following:

  Begin_Win_Critical_Section:
   push ax
   mov ax, 1681h
   int 2Fh
   test al, al
   jz BCS_Quick_Exit
   push es
   les ax, [InDOS_Address]
   inc BYTE PTR es:[ax]
   pop es
  BCS_Quick_Exit:
   pop ax
   ret

  End_Win_Critical_Section:
   push ax
   mov ax, 1682h
   int 2Fh
   test al, al
   jz ECS_Quick_Exit
   push es
   les ax, [InDOS_Address]
   cmp BYTE PTR es:[ax], 0
   je (Error handler routine)
   dec BYTE PTR es:[ax]
   pop es
  ECS_Quick_Exit:
   pop ax
   ret






INDEX
──────────────────────────────────────────────────────────────────────────

    A
ADDHDR, defined
AddInstanceItem service
AdjustInitEndPt function
Adjust_Execution_Time service
Adjust_Exec_Priority service
Allocate_Device_CB_Area service
Allocate_GDT_Selector service
Allocate_Global_V86_Data_Area service
Allocate_LDT_Selector service
Allocate_PM_Call_Back service
Allocate_Temp_V86_Data_Area service
Allocate_V86_Call_Back service
API translation

    B
BeginSelection function
Begin_Critical_Section service
Begin_Nest_Exec service
Begin_Nest_V86_Exec service
Begin_Reentrant_Execution service
Begin_Use_Locked_PM_Stack service
Break Point and Callback services
  Allocate_PM_Call_Back
  Allocate_V86_Call_Back
  Call_When_Idle
  Call_When_VM_Returns
  Install_V86_Break_Point
  Remove_V86_Break_Point
BuildDescDWORDs service
Build_Int_Stack_Frame service

    C
Callback procedures
Calling conventions, defined
Call_Global_Event service
Call_Priority_VM_Event service
Call_VM_Event service
Call_When_Idle service
Call_When_Not_Critical service
Call_When_Task_Switched service
Call_When_VM_Ints_Enabled service
Call_When_VM_Returns service
Cancel_Global_Event service
Cancel_Priority_VM_Event service
Cancel_Time_Out service
Cancel_VM_Event service
CB_High_Linear service
CheckGRBVersion function
Claim_Critical_Section service
ConsSelecRec function
Convert_Boolean_String service
Convert_Decimal_String service
Convert_Fixed_Point_String service
Convert_Hex_String service
CopyPageTable service
Crash_Cur_VM service
Create_Semaphore service
CursorOff function
CursorOn function
CursorPosit function

    D
DCP. See Device control procedure
DDB. See Device descriptor block
DeAssign_Device_V86_Pages service
Destroy_Semaphore service
Device control procedure, defined
Device descriptor block, defined
Disable_Global_Trapping service
Disable_Local_Trapping service
Disable_VM_Ints service
DPMI. See DOS protected mode interface

    E
Enable_Global_Trapping service
Enable_Local_Trapping service
Enable_VM_Ints service
EndSelection function
End_Critical_Section service
End_Crit_And_Suspend service
End_Nest_Exec service
End_Reentrant_Execution service
End_Use_Locked_PM_Stack service
Error Condition services
  Crash_Cur_VM
  Fatal_Error_Handler
  Fatal_Memory_Error
Event services
  Call_Global_Event
  Call_Priority_VM_Event
  Call_VM_Events
  Cancel_Global_Event
  Cancel_Priority_VM_Event
  Cancel_VM_Event
  Hook_NMI_Event
  Schedule_Global_Event
  Schedule_VM_Event
Event
  defined
Exec_Int service
Exec_VxD_Int service

    F
Fatal_Error_Handler service
Fatal_Memory_Error service
Free_GDT_Selector service
Free_LDT_Selector service
Free_Temp_V86_Data_Area service

    G
GetAppFlatDSAlias service
GetDemandPageInfo service
GetDisplayUpd function
GetDOSVectors service
GetFirstV86Page service
GetFontList function
GetFreePageCount service
GetNulPageHandle service
GetSetPageOutCount service
GetSet_HMA_Info service
GetSysPageCount service
GetVMPageCount service
Get_Config_Directory service
Get_Crit_Section_Status service
Get_Cur_VM_Handle service
Get_Device_V86_Pages_Array service
Get_Environment_String service
Get_Execution_Focus service
Get_Exec_Path service
Get_Last_Updated_System_Time service
Get_Last_Updated_VM_Exec_Time service
Get_Machine_Info service
Get_Next_Profile_String service
Get_Next_VM_Handle service
Get_PM_Int_Type service
Get_PM_Int_Vector service
Get_Profile_Boolean service
Get_Profile_Decimal_Int service
Get_Profile_Fixed_Point service
Get_Profile_Hex_Int service
Get_Profile_String service
Get_PSP_Segment service
Get_System_Time service
Get_Sys_VM_Handle service
Get_Time_Slice_Granularity service
Get_Time_Slice_Info service
Get_Time_Slice_Priority service
Get_V86_Int_Vector service
Get_VMM_Reenter_Count service
Get_VMM_Version service
Get_VM_Exec_Time service
Global descriptor table, defined
Grabber DLL functions
  AdjustInitEndPt
  BeginSelection
  CheckGRBVersion
  ConsSelecRec
  CursorOff
  CursorOn
  CursorPosit
  EndSelection
  GetDisplayUpd
  GetFontList
  GrabComplete
  GrabEvent
  GrbUnLockApp
  InitGrabber
  InvertSelection
  KeySelection
  MakeSelctRect
  PaintScreen
  RenderSelection
  ScreenFree
  SetPaintFnt
  UpdateScreen
Grabber
GrabComplete function
GrabEvent function
GrbUnLockApp function

    H
Hardware interrupt hooks
HeapAllocate service
HeapFree service
HeapGetSize service
HeapReAllocate service
Hook_Device_Service service
Hook_Device_V86_API service
Hook_NMI_Event service
Hook_PM_Device_API service
Hook_V86_Int_Chain service
Hook_V86_Page service

    I
I/O port traps
I/O services and macros
  Disable_Global_Trapping
  Disable_Local_Trapping
  Enable_Global_Trapping
  Enable_Local_Trapping
  Install_IO_Handler
  Install_Mult_IO_Handlers
  Simulate_IO
IDT. See Interrupt descriptor table
Information services
  GetSet_HMA_Info
  Get_Cur_VM_Handle
  Get_Next_VM_Handle
  Get_Sys_VM_Handle
  Get_VMM_Reenter_Count
  Get_VMM_Version
  Test_Cur_VM_Handle
  Test_Debug_Installed
  Test_Sys_VM_Handle
  Validate_VM_Handle
InitGrabber function
Initialization Information services
  Convert_Boolean_String
  Convert_Decimal_String
  Convert_Fixed_Point_String
  Convert_Hex_String
  GetDOSVectors
  Get_Config_Directory
  Get_Environment_String
  Get_Exec_Path
  Get_Machine_Info
  Get_Next_Profile_String
  Get_Profile_Boolean
  Get_Profile_Decimal_Int
  Get_Profile_Fixed_Point
  Get_Profile_Hex_Int
  Get_Profile_String
  Get_PSP_Segment
  OpenFile
Install_IO_Handler service
Install_Mult_IO_Handlers service
Install_V86_Break_Point service
INT 2FH
Interrupt descriptor table
  In protected mode
Interrupt request
InvertSelection function
IRQ. See Interrupt Request

    K
KeySelection function

    L
LDT. See Local descriptor table
Link386, defined
Linked List services
  List_Allocate
  List_Attach
  List_Attach_Tail
  List_Create
  List_Deallocate
  List_Destroy
  List_Get_First
  List_Get_Next
  List_Insert
  List_Remove
  List_Remove_First
LinMapIntoV86 service
LinPageLock service
LinPageUnLock service
List_Allocate service
List_Attach service
List_Attach_Tail service
List_Create service
List_Deallocate service
List_Destroy service
List_Get_First service
List_Get_Next service
List_Insert service
List_Remove service
List_Remove_First service
Local descriptor table, defined

    M
MakeSelctRect function
MapIntoV86 service
MapPhysToLinear service
MAPSYM32, defined
Map_Flat service
MASM5, defined
Memory Management services
  Data Access services
    GetAppFlatDSAlias
    GetFirstV86Page
Memory management services
  Data Access services
    GetNulPageHandle
Memory Management services
  Device V86 Page Management services
    Assign_Device_V86_Pages
    DeAssign_Device_V86_Pages
    Get_Device_V86_Pages_Array
    Hook_V86_Page
  GDT/LDT Management services
    Allocate_GDT_Selector
    Allocate_LDT_Selector
    BuildDescDWORDs
    Free_GDT_Selector
    Free_LDT_Selector
  Instance Data Management services
    AddInstanceItem
    MMGR_Toggle_HMA
  Physical Device Memory in PM services
    MapPhysToLinear
  Special services for PM APIs
    LinMapIntoV86
    LinPageLock
    LinPageUnLock
    PageCheckLinRange
    SelectorMapFlat
  System Data Object Management services
    Allocate_Device_CB_Area
    Allocate_Global_V86_Data_Area
    Allocate_Temp_V86_Data_Area
    Free_Temp_V86_Data_Area
  System Heap Allocator services
    HeapAllocate
    HeapFree
    HeapGetSize
    HeapReAllocate
  System Page Allocator services
    CopyPageTable
    GetDemandPageInfo
    GetFreePageCount
    GetSetPageOutCount
    GetSysPageCount
    GetVMPageCount
    MapIntoV86
    ModifyPageBits
    PageAllocate
    PageFree
    PageGetAllocInfo
    PageGetSizeAddr
    PageLock
    PageOutDirtyPages
    PageReAllocate
    PageUnLock
    PhysIntoV86
    TestGlobalV86Mem
  V86 Address Space services
    CB_High_Linear
Memory management, V86 mode
  API translation
Memory management
   defined
Message mode, defined
Miscellaneous services
  Begin_Reentrant_Execution
  End_Reentrant_Execution
  Hook_Device_Service
  Hook_Device_V86_API
  Hook_PM_Device_API
  Map_Flat
  MMGR_SetNULPageAddr
  Set_System_Exit_code
  Simulate_Pop
  Simulate_Push
  System_Control
MMGR_SetNULPageAddr service
MMGR_Toggle_HMA service
Mode
  Protected
  Real
  Virtual-86
ModifyPageBits service

    N
Nested Execution services
  Begin_Nest_Exec
  Begin_Nest_V86_Exec
  Begin_Use_Locked_PM_Stack
  End_Nest_Exec
  End_Use_Locked_PM_Stack
  Exec_Int
  Exec_VxD_Int
  Restore_Client_State
  Resume_Exec
  Save_Client_State
  Set_PM_Exec_Mode
  Set_V86_Exec_Mode
No_Fail_Resume_VM service
Nuke_VM service

    O
OpenFile service

    P
PageAllocate service
PageCheckLinRange service
PageFree service
PageGetAllocInfo service
PageGetSizeAddr service
PageLock service
PageOutDirtyPages service
PageReAllocate service
PageUnLock service
PaintScreen function
PhysIntoV86 service
PM. See Protected mode
Primary Scheduler services
  Adjust_Exec_Priority
  Begin_Critical_Section
  Call_When_Not_Critical
  Call_When_Task_Switched
  Claim_Critical_Section
  Create_Semaphore
  Destroy_Semaphore
  End_Critical_Section
  End_Crit_And_Suspend
  Get_Crit_Section_Status
  No_Fail_Resume_VM
  Nuke_VM
  Release_Critical_Section
  Resume_VM
  Signal_Semaphore
  Suspend_VM
  Wait_Semaphore
Privilege rings
Processor Fault and Interrupt services
  Get_Fault_Hook_Addrs
  Get_NMI_Handler_Addr
  Hook_NMI_Event
  Hook_PM_Fault
  Hook_V86_Fault
  Hook_V86_Page
  Hook_VMM_Fault
  Set_NMI_Handler_Addr
Protected mode
  defined
  Initialization
  Interrupt descriptor table

    R
Real mode
  defined
  initialization
Release_Critical_Section service
Release_Time_Slice service
Remove_V86_Break_Point service
RenderSelection function
Restore_Client_State service
Resume_Exec service
Resume_VM service

    S
Save_Client_State service
Schedule_Global_Event service
Schedule_VM_Event service
Scheduling
  defined
  Enhanced Windows execution
  Primary scheduler
  Time-slice scheduler
ScreenFree function
SelectorMapFlat service
Services, defined
SetPaintFnt function
Set_Execution_Focus service
Set_Global_Time_Out service
Set_PM_Exec_Mode service
Set_PM_Int_Type service
Set_PM_Int_Vector service
Set_System_Exit_code service
Set_Time_Slice_Granularity service
Set_Time_Slice_Priority service
Set_V86_Exec_Mode service
Set_V86_Int_Vector service
Set_VM_Time_Out service
Shell services
  SHELL_Event
  SHELL_Get_Version
  SHELL_Message
  SHELL_Resolve_Contention
Shell, defined
Signal_Semaphore service
Simulate_Far_Call service
Simulate_Far_Jmp service
Simulate_Far_Ret service
Simulate_Far_Ret_N service
Simulate_Int service
Simulate_IO service
Simulate_Iret service
Simulate_Pop service
Simulate_Push service
Software interrupt hooks
Suspend_VM service
System_Controls service

    T
TestGlobalV86Mem service
Test_Cur_VM_Handle service
Test_Debug_Installed service
Test_Sys_VM_Handle service
Time-Slice Scheduler services
  Adjust_Execution_Time
  Call_When_Idle
  Get_Execution_Focus
  Get_Time_Slice_Granularity
  Get_Time_Slice_Info
  Get_Time_Slice_Priority
  Release_Time_Slice
  Set_Execution_Focus
  Set_Time_Slice_Granularity
  Set_Time_Slice_Priority
  Wake_Up_VM
Timing services
  Cancel_Time_Out
  Get_Last_Updated_System_Time
  Get_Last_Updated_VM_Exec_Time
  Get_System_Time
  Get_VM_Exec_Time
  Set_Global_Time_Out
  Set_VM_Time_Out
  Update_System_Clock

    U
UpdateScreen function
Update_System_Clock service

    V
V86 Mode Memory Manager Device services
  V86MMGR_Allocate_Buffer
  V86MMGR_Allocate_V86_Pages
  V86MMGR_Free_Buffer
  V86MMGR_Free_Page_Map_Region
  V86MMGR_Get_EMS_XMS_Limits
  V86MMGR_Get_Mapping_Info
  V86MMGR_Get_Version
  V86MMGR_Get_VM_Flat_Sel
  V86MMGR_Get_Xlat_Buff_State
  V86MMGR_Load_Client_Ptr
  V86MMGR_Map_Pages
  V86MMGR_Set_EMS_XMS_Limits
  V86MMGR_Set_Mapping_Info
  V86MMGR_Set_Xlat_Buff_State
  V86MMGR_Xlat_API
V86. See Virtual-86 mode
V86MMGR services. See V86 Mode Memory Manager Device services
Validate_VM_Handle service
VDD services. See Virtual Display Device services
VDMAD services. See Virtual DMA Device services
VDMAD_Request_Buffer service
Virtual device
  API procedure
  API
  Building
  Declaration
  defined
  Device control procedure name
  Device control procedure
  Device descriptor block
  ID
  IDS
  Initialization order
  Initialization
  Installing
  Memory model
  Segmentation
  Service table
  Services
  Version
  Writing strategy
Virtual Display Device services
  VDD_Get_GrabRtn
  VDD_Get_ModTime
  VDD_Get_Version
  VDD_Hide_Cursor
  VDD_Msg_BakColor
  VDD_Msg_ClrScrn
  VDD_Msg_ForColor
  VDD_Msg_SetCursPos
  VDD_Msg_TextOut
  VDD_PIF_State
  VDD_Query_Access
  VDD_Set_HCurTrk
  VDD_Set_VMType
Virtual DMA Device services
  VDMAD_Copy_From_Buffer
  VDMAD_Copy_To_Buffer
  VDMAD_Default_Handler
  VDMAD_Disable_Translation
  VDMAD_Enable_Translation
  VDMAD_Get_EISA_Adr_Mode
  VDMAD_Get_Region_Info
  VDMAD_Get_Version
  VDMAD_Get_Virt_State
  VDMAD_Lock_DMA_Region
  VDMAD_Mask_Channel
  VDMAD_Release_Buffer
  VDMAD_Request_Buffer
  VDMAD_Reserve_Buffer_Space
  VDMAD_Scatter_Lock
  VDMAD_Scatter_Unlock
  VDMAD_Set_EISA_Adr_Mode
  VDMAD_Set_Phys_State
  VDMAD_Set_Region_Info
  VDMAD_Set_Virt_State
  VDMAD_Unlock_DMA_Region
  VDMAD_UnMask_Channel
  VDMAD_Virtualize_Channel
Virtual DOSNET device services
  DOSNET_Do_PSP_Adjust
  DOSNET_Get_Version
  DOSNET_Send_FILESYSCHANGE
Virtual Keyboard Device services
  VKD_Cancel_Hot_Key_State
  VKD_Cancel_Paste
  VKD_Define_Hot_Key
  VKD_Define_Paste_Mode
  VKD_Flush_Msg_Key_Queue
  VKD_Force_Keys
  VKD_Get_Kbd_Owner
  VKD_Get_Msg_Key
  VKD_Get_Version
  VKD_Local_Disable_Hot_Key
  VKD_Local_Enable_Hot_Key
  VKD_Peek_Msg_Key
  VKD_Reflect_Hot_Key
  VKD_Remove_Hot_Key
  VKD_Start_Paste
Virtual machine manager, defined
Virtual machine
  Client Register structure
  defined
  Events
  Initialization
  Loading sequence
  Privilege rings
  Scheduling
  States
  Termination
  VM handle
Virtual PIC Device services
  VID_EOI_Proc
  VID_Hw_Int_Proc
  VID_IRET_Proc
  VID_Mask_Change_Proc
  VID_Virt_Int_Proc
  VPICD_Call_When_Hw_Int
  VPICD_Clear_Int_Request
  VPICD_Convert_Handle_To_IRQ
  VPICD_Convert_Int_To_IRQ
  VPICD_Convert_IRQ_To_Int
  VPICD_Get_Complete_Status
  VPICD_Get_IRQ_Complete_Status
  VPICD_Get_Status
  VPICD_Get_Version
  VPICD_Physically_Mask
  VPICD_Physically_Unmask
  VPICD_Phys_EOI
  VPICD_Set_Auto_Masking
  VPICD_Set_Int_Request
  VPICD_Test_Phys_Request
  VPICD_Virtualize_IRQ
Virtual PIC Device
  defined
Virtual Sound Device services
  VSD_Bell
  VSD_Get_Version
Virtual Timer Device services
  VTD_Begin_Min_Int_Period
  VTD_Disable_Trapping
  VTD_Enable_Trapping
  VTD_End_Min_Int_Period
  VTD_Get_Interupt_Period
  VTD_Get_Version
  VTD_Update_System_Clock
Virtual-86 mode, defined
VKD services. See Virtual Keyboard Device services
VM Interrupt and Call services
  Build_Int_Stack_Frame
  Call_When_Idle
  Call_When_VM_Ints_Enabled
  Disable_VM_Ints
  Enable_VM_Ints
  Get_PM_Int_Type
  Get_PM_Int_Vector
  Get_V86_Int_Vector
  Hook_V86_Int_Chain
  Set_PM_Int_Type
  Set_PM_Int_Vector
  Set_V86_Int_Vector
  Simulate_Far_Call
  Simulate_Far_Jmp
  Simulate_Far_Ret
  Simulate_Far_Ret_N
  Simulate_Int
  Simulate_Iret
VM. See Virtual machine
VMM. See Virtual machine manager
VPICD services. See Virtual PIC Device services
VSD services. See Virtual Sound Device services
VTD services. See Virtual Timer Device services
VxD. See Virtual device

    W
Wait_Semaphore service
Wake_Up_VM service
WINOLDAP