
             The QEMM Programming Interface - QPI

 Quarterdeck Technical Note #292             Filename: QPI.TEC
 by Quarterdeck Development                CompuServe: QPI.TEC
 Last Revised: 04/05/95                      Category: QEMM

 Subject: A discussion on programming issues when interfacing
          with QEMM.


 QPI:The QEMM Programming Interface

 The QEMM Programming Interface (QPI) lets programs request
 information or services from QEMM. Programs can use the QPI to do
 the following:

 Determine QEMM's status, and change that status if the system
 configuration allows;

 Determine QEMM's version number;

 Determine whether QEMM's Stealth ROM feature is active, and if so
 what Stealth ROM mode is in use;

 Determine the number of ROMs that QEMM is Stealthing and the
 beginning segment address and length of each ROM;

 Determine whether QEMM is supporting the system's Suspend/Resume
 features, and if so what interrupt these features are using;

 Determine whether QEMM is allowing or suppressing the BIOS calls
 that make it possible to do work while waiting for disk activity
 to complete, and tell QEMM to allow or suppress these calls if the
 system configuration allows;

 Copy all or part of the contents of a Stealthed ROM into a buffer;

 Determine the physical memory mapped to any linear memory address,
 and change the page table so that any page of physical memory is
 mapped to any linear memory address;

 Read or write I/O ports, even if QEMM is trapping access to those
 ports;

 Determine whether QEMM is trapping access to I/O ports, and tell
 QEMM to trap access to ports on the calling program's behalf;

 Install a software routine that performs whatever actions the
 program requires when a given I/O port is accessed;

 Simulate a hardware interrupt in such a way that it goes to the
 correct DESQview or DESQview/X window.

 You can find sample QPI code and programs on the Quarterdeck
 bulletin board and other electronic support locations.

 Getting the QPI Entry Point

 The first step in using the QPI is getting the double-word address
 of the QPI entry point.

 The method of obtaining the entry point address that is described
 here is available only in versions of QEMM higher than 6.00.
 Programs that want to run with QEMM 5 must use a less
 straightforward INT 2F interface to get the QPI entry point; for
 more information, contact Quarterdeck via Compuserve (GO
 QUARTERDECK), the Internet (support@qdeck.com) or our BBS and ask
 for the QDMEM interface document.

 QEMM defines a DOS device driver called QEMM386$. To obtain the
 entry point for QPI, do an IOCTL Read Control String call (INT 21,
 function 4402h) to read four bytes from this device driver. Here
 is a code sequence that demonstrates the details:


 QEMMDeviceName db 'QEMM386$',0
 QPIEntryPoint  dd ?

 GetQPIEntryPoint proc
   mov  dx,offset QEMMDeviceName
   mov  ax,3d00h
   int  21h                      ; Try to open QEMM386$
   jc  NoQEMM                    ; If CY, QEMM not present
   mov  bx,ax                    ; Save file handle in BX
   mov  dx,offset QPIEntryPoint  ; Store the entry point here
   mov  cx,4                     ; Set up to read 4 bytes
   mov  ax,4402h                 ; IOCTL Read Control String
   int  21h
   pushf                         ; Save the error code
   mov  ah,3eh                   ; Close the handle
   int  21h
   popf                          ; Restore the error code
   jc  NoQEMM                    ; If CY, QEMM is pre-6.00
   ret
 NoQEMM: stc
   ret
 GetQPIEntryPoint endp


 QPI Functions

 Once you have stored the address of the QPI entry point, you make
 all calls to QPI by loading AH or AX with the function number of
 the call, setting the other registers to values appropriate to the
 function, and making a far call to the entry point. The carry flag
 is set on return if there is an error, or if the function number
 is not valid in that version of QEMM.

 The QPI calls of interest to third-party programmers are listed
 below. The version in which each of the calls was implemented is
 noted.

 The QPI_GetStatus Call

 The QPI_GetStatus call tells you whether QEMM is on or off, and
 whether it is in auto mode (see the AUTO/ON/OFF parameter in
 Chapter 7 in the QEMM Reference Manual for more information). All
 versions of QEMM support this call.


 QPI_GetStatus    EQU   0
    ; Takes    AH = 0
    ; Returns   AL = 0 if on
    ;       AL = 1 if auto/on
    ;       AL = 2 if off
    ;       AL = 3 if auto/off


 The QPI_SetStatus Call

 The QPI_SetStatus call lets you set the status of QEMM. If QEMM is
 forced on by a parameter (like the RAM parameter) or other
 services that it provides, this call will have no effect. You
 should therefore make the QPI_GetStatus call after the
 QPI_SetStatus call to see if the first call was successful. All
 versions of QEMM support this call.

 QPI_SetStatus    EQU   1
     ; Takes   AH = 1
     ;      AL = 0 if on
     ;      AL = 1 if auto/on
     ;      AL = 2 if off
     ;      AL = 3 if auto/off


The QPI_GetVersion Call

 The QPI_GetVersion call returns the QEMM version number in Binary
 Coded Decimal form in AX and BX. For instance, the call will
 return BX = 0750 (not BX = 0732) for QEMM version 7.5.  All
 versions of QEMM support this call.

 QPI_GetVersion    EQU   3
     ; Takes   AH = 3
     ; Returns  BH = major version (in Binary Coded Decimal)
     ;      BL = minor version (in Binary Coded Decimal)
     ;      AX = same as BX



The QPI_GetInfo Call

 In QEMM version 6.00 and later, the QPI_GetInfo call returns an
 ASCII letter in CL that tells QEMM's Stealth ROM mode (if any),
 and a number in CH that tells which interrupt (not IRQ) QEMM is
 monitoring (if any) to support Suspend/Resume features. In QEMM
 version 7.00 and later, the call also returns the size of QEMM's
 disk buffer in DL and a bit map of information about the disk
 buffer in BH. Bit 1 of BH will be on if the disk buffer has
 already been used; bit 0 will be on if QEMM is buffering only INT
 13s into the page frame (DISKBUFFRAME) and off if all INT 13s into
 nonlinear memory are being buffered (DISKBUF). Note that other
 registers are not preserved by this call.


 QPI_GetInfo     EQU   1E00h
     ; Takes    AX = 1E00
     ; Returns  BH = xxxxxxAB
     ;    where A = 1 if disk buffer has been used yet, 0 if not
     ;          B = 1 if DISKBUFFRAME buffer, 0 if DISKBUF buffer
                       - not valid if DL = 0
     ;          BL = reserved
     ;          CL = Stealth ROM type (0 for no Stealth ROM, "M"
     ;               or "F" otherwise, other Stealth ROM types possible
     ;               in future)
     ;          CH = Suspend/Resume INT number (0 = none)
     ;          DL = size of QEMM disk buffer in K (if 0, disk buffer
     ;               doesn't exist)
     ;          DH, DI, SI = reserved

 The QPI_GetStealthCount Call

 The QPI_GetStealthCount tells how many ROMs QEMM is Stealthing.
 QEMM versions 6.00 and later support this call.


 QPI_GetStealthCount equ 1E01h
     ;   Takes  AX = 1E01
     ; Returns  BX = number of ROMs that are Stealthed


 The QPI_GetStealthList Call

 The QPI_GetStealthList call gives the same information as the
 QPI_GetStealthCount call, and also fills a buffer with information
 on the location and size of each Stealthed ROM. QEMM versions 6.00
 and later support this call.


 QPI_GetStealthList equ 1E02h
     ;   Takes   AX = 1E02
     ;        ES:DI = buffer to hold the list of Stealthed ROMs
     ; Returns   BX = number of ROMs that are Stealthed
     ;     Table at ES:DI will be filled in with:
     ;       dw ROM start segment
     ;       dw Length of ROM in paragraphs
     ;     for each ROM that is Stealthed


 The QPI_GetPTE Call

 The QPI_GetPTE call returns the page table entry for any logical
 page in the first 1088K of memory. In other words, if you pass
 this call the address of any page in the first 1088K of memory,
 the call will return (in an extended register) the doubleword page
 table entry for that address, which includes, among other things,
 information about which physical page of memory QEMM has mapped to
 the logical address that you provided. QEMM versions 6.00 and
 later support this call.

 CX should contain the number of the logical page in memory that
 you want to affect; the highest valid CX is 010F. (Page numbers
 refer to consecutive 4K sections of memory, aligned on 4K
 boundaries: that is, 0000 refers to paragraph 0000-00FF, 0001 to
 0100-01FF, etc.) EDX (the extended DX register) should contain a
 page table entry, in the following format:

 Bit 0 is the Present bit. Any access to a page with this bit off
 causes a page fault.

 Bit 1 is the Read/Write bit. Any writing to a page with this bit
 off causes a page fault.

 Bit 2 is the User/Supervisor bit. Any access to this page when the
 processor is at privilege level 3 causes a page fault.

 Bits 3 and 4 must be 0.

 Bit 5 is the Accessed bit. Any read or write of the page causes
 the processor to turn on this bit.

 Bit 6 is the Dirty bit. Any write to the page causes the processor
 to turn on this bit.

 Bits 7 and 8 must be 0.

 Bits 9, 10, and 11 are available for systems programmer use.

 Bits 12 through 31 are the page number.

 For instance, a page table entry 000FF007 means physical page
 number 000FF (paragraph FF00-FFFF), which is not yet accessed nor
 dirty, but which is present, writable and user-accessible.


 QPI_GetPTE      equ 1F00h
     ;   Takes   AX = 1F00
     ;           CX = page number
     ; Returns  EDX = page table entry for that page number


 The QPI_SetPTE Call

 The QPI_SetPTE call lets you set the page table entry for any
 logical page in the first 1088K of memory. In particular, this
 means that you can tell QEMM to map any 4K of memory to any
 4K-aligned address below the 1088K mark. QEMM versions 6.00 and
 later support this call. See the section above on the QPI_GetPTE
 call for more information.


 QPI_SetPTE     equ   1F01h
     ; Takes  AX = 1F01
     ;        CX = page number
     ;       EDX = page table entry to set at that page number


 The QPI_GetVHIInfo Call

 The QPI_GetVHIInfo call, in conjunction with the QPI_SetVHIInfo
 call, is primarily used by disk cache developers who wish to get
 information on QEMM's safety precaution of suppressing the BIOS
 INT 15 function 90 callout. This safety precaution, known as
 VirtualHDIRQ or VHI (see the section on the VIRTUALHDIRQ:N
 parameter in Chapter 7 in the QEMM Reference Manual for more
 information), is normally in effect only when the disk interrupt
 INT 13 is being Stealthed. If you have verified that the use of
 INT 15 fn 90 in the disk cache you are developing is compatible
 with QEMM's Stealth ROM feature, you will want to tell QEMM to
 allow INT 15 function 90.

 The call returns flags in BL that give the VHI state. Bit 7 will
 be on whenever QEMM is Stealthing INT 13 (if INT 13 is not
 Stealthed, QEMM never suppresses INT 15 function 90); bit 0 will
 be on if QEMM is currently suppressing INT 15 function 90. Bits
 1-6 of the VHI bit map are reserved. QEMM versions 6.00 and later
 support this call.


 QPI_GetVHIInfo   equ   2000h
     ;  Takes   AX = 2000
     ; Returns  BL = AxxxxxxB
     ;     where A = 1 if VHI is being paid attention to
     ;           B = 1 if VHI is currently enabled
     ;              (i.e. INT 15 fn 90 is currently suppressed)
     ;           x = reserved

 The QPI_SetVHIInfo Call

 The QPI_SetVHIInfo call lets you turn on or off QEMM's safety
 precaution of suppressing INT 15 function 90 whenever the disk
 interrupt INT 13 is being Stealthed. (See the section above on the
 QPI_GetVHIInfo call for more information.) To request a VHI state,
 set BL to 1 to suppress INT 15 function 90, or set BL to 0 to
 allow INT 15 fn 90. QEMM will return the previous VHI flags in BL.
 If bit 7 of the returned flags is off, then QEMM is not paying
 attention to the VHI state, and your request did not have an
 effect. QEMM versions 6.00 and later support this call.


 QPI_SetVHIInfo   equ   2001h
     ;   Takes   AX = 2001
     ;           BL = xxxxxxxB that you want (bit 7 is ignored)
     ; Returns   BL = AxxxxxxB of previous VHI state;
     ;           if A of output = 0, B of input was ignored


 The QPI_CopyStealthRoms Call

 The QPI_CopyStealthRoms call tells QEMM to copy the contents of
 part or all of a Stealthed ROM into a buffer in conventional
 memory. This is the only reliable to access the contents of a
 Stealthed ROM. QEMM versions 6.00 and later support this call.


 QPI_CopyStealthRoms equ   2100h
     ; Takes   AX = 2100
     ;      DS:SI = Original address of ROM to copy
     ;      ES:DI = Destination address in conventional memory
     ;      ECX = # of bytes to copy
     ; Returns  CY if no stealth or if DS:SI not within C000-FFFF


 I/O Trapping

 The following calls make up the Quarterdeck QEMM I/O Trapping
 Programming Interface. This interface allows a real-mode program
 to specify I/O ports that QEMM should trap access to, as well as
 I/O callback routines that QEMM will call whenever one of these
 I/O ports is accessed. Using this interface, you can emulate
 hardware devices that are accessible via I/O ports.

 When QEMM traps an I/O port, all accesses of that port, whether
 input or output, are intercepted by QEMM. (QEMM traps certain I/O
 ports itself, for proper management of virtual-8086 mode.)
 Whenever an I/O port that a program has asked QEMM to trap is
 accessed, QEMM calls a real-mode I/O callback routine. The same
 callback routine is called for all trapped I/O ports.

 A program that wishes to trap an I/O port should:

 1) Use the QPI_GetVersion call to make sure that the version of
    QEMM is 7.03 or later. Earlier versions do not support most of
    the I/O Trapping Programming Interface. Alternatively, if the
    version of QEMM does not support the call you have made, the
    call will return with the carry flag set;

 2) Issue a QPI_GetPortTrap call to determine that another program
    is not already trapping the port. If another program is
    trapping the port, it is generally advisable not to install
    your port trap;

 3) Get the address of the existing callback routine with the
    QPI_GetIOCallback call. Because there is only one I/O callback
    routine, and because multiple programs may request I/O
    trapping, your callback routine must jump to the previous
    callback routine whenever your routine is not interested in the
    I/O port being accessed;

 4) Install its own far routine as the new callback routine, using
    the QPI_SetIOCallback call. Your callback routine will be
    passed the following information:

     AX = Data for output
     CX = Type of I/O (see flag bits defined below)
     DX = Port number
     IF = 0 (interrupts are disabled)

 When the callback routine has finished its work, it should return
 far with all registers other than CX and DX preserved. If the
 routine is called to get input from a port, AX should be modified.
 The bit-mapped word in CX contains the following information:


 IOT_Output     equ   0000000000000100b
     ;      bit 2 is 1 if output,
     ;      0 if input


 IOT_Word      equ   0000000000001000b
     ;      bit 3 is 1 if word I/O,
     ;      0 if byte I/O


 IOT_IF       equ   0000001000000000b
     ;      bit 9 is the same as the
     ;      caller's interrupt flag


 5) Specify which port to trap with the QPI_SetPortTrap call.

    If your program does not stay resident forever, it should do
    the following before exiting:

 6) Use the QPI_GetIOCallback call to make sure that no one has
    installed a callback routine after yours. If a handler is
    installed after yours, you should remain resident;

 7) Remove its trap with the QPI_ClearPortTrap call. This call will
    clear all traps on a particular I/O port, unless QEMM is
    trapping that port for itself, in which case QEMM's trapping
    alone will remain in effect for that port;

 8) Remove its callback by using the QPI_SetIOCallback call to set
    the previously existing callback routine.

 The following restrictions apply to this interface:

 Only INs and OUTs of words or bytes are supported, not INs and
 OUTs of doublewords. Also, string I/O (the INS and OUTS
 instructions) are supported only as of QEMM version 7.5.

 QEMM cannot trap the I/O of VCPI protected-mode programs.
 Furthermore, because Quarterdeck's DPMI driver (QDPMI) is
 implemented as a VCPI client, QEMM cannot trap the I/O of DPMI
 clients either. Furthermore, these traps are no longer in effect
 when Microsoft Windows 386 enhanced mode is running.

 The following four calls allow you to bypass port trapping and
 read and write I/O ports directly. QEMM 5.00 and later versions
 support these four calls.


 QPI_UntrappedIORead equ   1A00h
     ; Takes   AX = 1A00
     ;         DX = port to read
     ; Returns BL = value read


 QPI_UntrappedIOWrite equ   1A01h
     ; Takes   AX = 1A01
     ;         DX = port to write
     ;         BL = value to write


 QPI_UntrappedIOReadIndexed  equ   1A02h
     ; Takes   AX = 1A02
     ;         DX = base port to read
     ;         BH = index into base port
     ; Returns BL = value read


 QPI_UntrappedIOWriteIndexed  equ   1A03h
     ; Takes   AX = 1A03
     ;         DX = base port to write
     ;         BH = index into base port
     ;         BL = value to write


 The QPI_UntrappedIO Call

 The QPI_UntrappedIO call performs the same functions as the
 QPI_UntrappedIORead and the QPI_UntrappedIOWrite calls, but it
 uses register values that are similar to the ones QEMM passes to
 your I/O callback routine (including the flags in CX that give
 information about the type and size of the I/O and the caller's
 interrupt flag). This call may therefore be easier to use from
 within an I/O callback routine. QEMM 7.03 and later support this
 call.


 QPI_UntrappedIO   equ   1A04h
     ; Takes   AX = 1A04
     ;         BX = value to write
     ;         DX = port to read or write
     ;         CX = type of I/O (see description above
     ;              of CX passed to callback routine)
     ; Returns BX = value read

 The following five calls are described in the introduction above.
 QEMM 7.03 and later versions support these calls.


 QPI_GetIOCallback  equ   1A06h
     ;    Takes    AX = 1A06
     ; Returns  ES:DI = previous I/O callback function


 QPI_SetIOCallback  equ   1A07h
     ; Takes   AX = 1A07
     ;      ES:DI = new I/O callback function


 QPI_GetPortTrap   equ   1A08h
     ; Takes   AX = 1A08
     ;         DX = I/O port number
     ; Returns BL = 0 if port not trapped,
               BL = 1 if port already trapped


 QPI_SetPortTrap   equ   1A09h
     ; Takes   AX = 1A09
     ;         DX = I/O port number


 QPI_ClearPortTrap  equ   1A0Ah
     ; Takes   AX = 1A0A
     ;         DX = I/O port number


 The QPI_SimulateHWInt Call

 The QPI_SimulateHWInt call can be used by callback routines that
 wish to simulate a hardware interrupt. When DESQview or DESQview/X
 is running, the interrupt handler that should receive the
 interrupt may be in a different process from the current one. Use
 QPI_SimulateHWInt to simulate an interrupt properly when DESQview
 or DESQview/X is running. The DESQview API Reference Manual
 describes how to determine when DESQview is running. QEMM 7.03 and
 later versions support this call.

 QPI_SimulateHWInt  equ   1C04h
     ; Takes   AX = 1C04
     ;         BX = interrupt number to generate


 The QPI Get Sub-Version String Call

 QPI_Get_QEMM_SubVer equ 1E05h

     ; Takes      AX = 1E05
     ;            CX = Length of buffer
     ;         ES:DI = buffer
     ; Returns    CX = number of bytes which could not fit in buffer
     ;            CY if function not supported
     ; Fills in the given buffer with a null terminated string.  If
     ; the actual length of the string is greater than the length of
     ; the buffer (as passed in CX) then it clips the string to the
     ; buffer length - 1 and puts a null in the last byte of the buffer
     ; and returns the number of extra bytes in CX.  If everything
     ; fits in the buffer, then the returned CX=0.

 ******************************************************************
 *      Trademarks are property of their respective owners.       *
 *   This and other technical notes may be available in updated   *
 *     forms through Quarterdeck's standard support channels.     *
 *          Copyright (C) 1996 Quarterdeck Corporation            *
 ******************** E N D   O F   F I L E ***********************

