File:    PDR1.ZIP
Author:  Patrick Reilly (Compuserve 71333,2764)
Release: v0.9  2/28/95

This file contains a C library of functions for accessing the DPMI version 0.9
API. For implementation, I've written all the source files in assembly. I used
TASM and TASM32, version 4.0 to build these. I've included two files to make
these libraries (MAKEFILE.16 and MAKEFILE.32), which I ran with MAKE version
3.7.

Contents: this .zip file contains a multitude of .asm files (one for each C
function represented), DPMI.H, and the make files. The .asm files are split
up in this fashion so that when you link with the built DPMIC16.lib or 
DPMIC32.LIB, you will only link the actual functions you need (and not one
big module that you might only be using a small percentage of).

I wrote and compiled this library with BC++4.5, TASM/32 4.0, and MAKE3.7. I
haven't had a chance to debug all of it, so if you run into problems leave me
a note and I'll try to fix it. However, I do not provide design and use
support for this library; if you have DPMI questions, you should address them
to the BCPPDOS forum, the DPMI section (7).

This library is provided as is, and I am not responsible for your usage nor for
any problems you encounter with it. It is provided into the public domain - that
is, it's "freeware".

I wrote this library following the DPMI 0.9 specs as published by Intel (r)
dated July 26, 1990. A friend obtained a copy of this document for me off the
internet, so I'm not sure where it's available. You can try the INTELFORUM on
compuserve, or browse the net. The text file name is DPMISPEC.TXT, although I
would assume that in electronic form it is zipped up.

This file was zipped up using PKZIP version 2.04g. PKZIP is a registered
trademark of PKWARE.

The Library

DPMI.H

This header file defines the types and function prototypes for the library.
The following are the types defined:

----------------------------------------------------------------------------

typedef DPMIFAR

  For DPMI16, this expands to the __far keyword. For DPMI32, it expands to
  nothing.

struct tagPMRegs
typedef PMRegs
typedef LPPMRegs

  PMRegs defines a data structure that encapsulates the 80x86 register set in a
  format directly accessible to the DPMI 0.9 translation services. PMRegs is
  actually made up of a union of the three structures PMByteRegs, PMWordRegs and
  PMDWordRegs, which allow access to the 8-bit registers, 16-bit registers and
  32-bit registers respectively. The PMByteRegs member is named h, the
  PMWordRegs member is named w, and the PMDwordRegs registers is named x. So to
  access an 8-bit register (say, the AL registers) from a PMRegs variable obj,
  you would use obj.h.al = 3. For a 16-bit register (say AX), you would use
  obj.w.ax = 3; for a 32-bit (say EAX), you would use obj.x.eax = 3.
  The following members are common to all three structures: flags, es, ds, fs,
  gs, ip, cs, sp, ss. This means that you could access the ES representation,
  say, via either obj.h.es, obj.w.es, or obj.x.es.
  You might wonder why ESP and EIP are not represented in this structure (or
  usable in the DPMI 0.9 translation functions that use PMRegs). This is because
  the translation functions are used to access real mode functions - which
  operate in the 1MB real mode memory space, and use a 16-bit IP and stack.
  As in Windows notation, LPPMRegs is a (far) pointer to a PMRegs object.

typedef PMSelector
typedef RMSegment

  These two 16-bit types represent a selector (only used in protected mode) and
  a segment (only used in real mode). You could easily do without these simple
  typedefs (use unsigned short instead), but they allow your code to look
  cleaner, since at a glance you can tell whether an object can be used with
  protected or real mode.


typedef LinearAddress

  This 32-bit type represents a linear memory address. By linear, this means
  that for any two LinearAddress objects x, y=x+1, the memory referenced by 'y'
  is exactly one byte above the memory referenced by 'x'. In other words, this
  is NOT a combination of a segment:offset pair as is used in real mode memory
  referencing.

typedef PMHandle

  This 32-bit type represents a handle used by some DPMI linear memory
  functions.

typedef RMAddress
define RM_SEGMENT
define RM_OFFSET

  This 32-bit type represents a RMSegment:offset pair, where the high word is
  the RMSegment, and the low word is the offset. Note that this is NOT the same
  as a LinearAddress type! If you were using windows.h and wanted to convert a
  RMAddress to a LinearAddress, you would use:
   RMAddress rmaddr = ...;
   LinearAddress addr = (((LinearAddress)HIWORD(rmaddr)) << 4) + LOWORD(rmaddr);
  Note that a RMAddress can only reference up to (1MB+64KB-1), whereas a
  LinearAddress can reference up to the full 32-bit memory range.
  Included are two macros to extract the RMSegment and offset from a RMAddress:
   RM_SEGMENT(x) will evaluate to a RMSegment, and is the segment of x.
   RM_OFFSET(x) will evaluate to an unsigned short and is the offset of x.

struct tagDPMIVersion
typedef DPMIVersion
typedef LPDPMIVersion
enum version_flags

  This is version information for the DPMI host. Members are:
        major       8-bit       Major version number (ie 0x01 == version 1.xx)
        minor       8-bit       Minor version number (ie 0x09 == version x.09)
        flags       16-bit      See version_flags enum below
        cpu         8-bit       CPU version (0x02 = 80286, 0x03 = 80386, etc)
        masterPic   8-bit       Virtual master PIC state.
        slavePIC    8-bit       Virtual slave PIC state.
  The version_flags enumeration lists flags that can be or'd with DPMIVersion
  flags to boolean flag states. These flags are:
        isDPMI32                Non-zero if DPMI32 is the host.
        hasRealModeInterrupts   Non-zero if real mode interrupts (or V86 mode)
                                are used.
        hasVirtualMemory        Non-zero if virtual memory is supported.
  LPDPMIVersion types a (far) pointer to a DPMIVersion object.

struct tagDPMIMemInfo
typedef DPMIMemInfo
typedef LPDPMIMemInfo

  This is the memory information for the DPMI host. Members are:
        maxFreeBlocks           Largest available free block in bytes.
        maxUnlockedPage         Maximum unlocked page allocation.
        maxLockedPage           Maximum locked page allocation.
        linearPages             Linear address space size in pages.
        totalUnlockedPages      Total number of unlocked pages.
        totalFreePages          Number of free pages.
        totalPhysicalPages      Total number of physical pages.
        freeLinearPages         Free linear address space in pages.
        partitionSize           Size of paging file/partition in pages.
        reserved[]              Unused.

struct tagDPMIInfo
typedef DPMIInfo
typedef LPDPMIInfo

  This is the DPMI host information. Members are:
        supports32Bit           Non-zero if the host supports DPMI32.
        cpu                     See the same entry under DPMIVersion.
        major                   See the same entry under DPMIVersion.
        minor                   See the same entry under DPMIVersion.

typedef PMRights
enum pmrights_types

  This 16-bit type represents the access rights for a PMSelector. It is really a
  bit array, whose members are accessed via the pmrights_types enumerations.
  They are:
        rights_Accessed
        rights_ReadWrite
        rights_Conforming
        rights_Code
        rights_Reserved1       (not used by user)
        rights_Level           (2-bits)
        rights_Present
        rights32               (mask for all DPMI32 rights bits)
        rights32_Reserved2     (32-bit; not used by user)
        rights32_Avl           (32-bit)
        rights32_Reserved3     (32-bit; not used by user)
        rights32_Default32     (32-bit)
        rights32_PageGranular  (32-bit)

typedef Descriptor
typedef LPDescriptor
typedef LPCDescriptor

  This type (an 8-byte character array) represents a protected mode descriptor.

PMFarAddress

  This type represents a far protected mode pointer. In DPMI16, it is just a
  void __far*. However, in DPMI32, it is a structure representing a 48-bit
  pointer. In DPMI32, the first 32-bits of this structure are the offset of the
  pointer, and the last 16-bits are the selector.

typedef DosMemory
typedef LPDosMemory
define DM_SEGMENT
define DM_SELECTOR

  This 32-bit type represents a bit-packed structure of a PMSelector and a
  RMSegment (returned by dpmi_allocateDosMem() function). Two macros are
  provided to extract the members:
   DM_SEGMENT(x) evaluates to the RMSegment of the object.
   DM_SELECTOR(x) evaluates to the PMSelector of the object.

typedef WatchPoint
enum watchpoint_types

  This 16-bit type represents a watch point "handle". The watchpoint_types
  enumeration represents flags used with the watch point functions.

----------------------------------------------------------------------------

The following are descriptions of the function groups, and the prototypes for
the functions:

Mode Detection
--------------

  These functions are used to determine if a DPMI host is present, and if the
  CPU is currently running in protected mode. Note that ALL functions except
  dpmi_present() assume that a DPMI host IS available; if one is not available,
  the functions can crash.

int DPMIFAR dpmi_present( LPDPMIInfo info );

  Returns 0 if a DPMI host was not detected, else returns 1. If info is
  non-null, then info will be filled with the host information.

int DPMIFAR dpmi_running(void);

  Returns 0 if the CPU is in real mode, else 1 if in protected mode.

LDT Descriptor Management Services
----------------------------------

  These functions allow you to allocate, free, and manipulate protected mode
  selectors and descriptors.

PMSelector DPMIFAR dpmi_allocateSelector( unsigned short count );

  Tries to allocate <count> congruent selectors. On success, returns the first
  of the allocated selectors; on failure, returns 0. The selectors have no
  limit or size, although they do have "standard" data rights. You can use
  dpmi_setSelectorLinearAddress() and dpmi_setSelectorLimit() to give the
  selectors a size and limit. If <count> is greater than one, then the
  selectors are congruent; you can calculate the following selector by calling
  dpmi_getSelectorInc() and adding the returned value to a selector. For
  example:
    PMSelector first, second;
    unsigned short inc;
    first = dpmi_allocateSelector(2);
    inc = dpmi_getSelectorInc();
    second = first+inc;
    ...
  Use dpmi_freeSelector() to free selectors allocated with this function. If
  you allocate more than one selector (count > 1), then you ONLY free the
  selector returned by this function (you do NOT free any of the other
  selectors in the array!).
  Remember that there are normally only 8192 selectors available for allocation!

int DPMIFAR dpmi_freeSelector( PMSelector sel );

  Frees a selector (or congruent array of selectors). Returns 1 on success,
  or 0 on failure. Use this function to free selectors allocated with
  dpmi_allocateSelector(), dpmi_aliasCodeToData(), or
  dpmi_allocateSpecificSelector().

PMSelector DPMIFAR dpmi_segmentToSelector( RMSegment seg );

  Returns a selector for a particular real mode segment address. Returns the
  selector on success, or 0 on failure. Note that these selectors are limited
  resources, and that you must NOT free, or modify, the returned selector. The
  selector will have a linear base address corresponding to <seg>:0000h real
  mode memory, and a limit of 64KB. If you call this function more than once
  with the same <seg> value, then the same selector will be returned. For
  example: if you want to access the video memory at real mode address A000:0000
  you could use:
    PMSelector sel = dpmi_segmentToSelector( 0xA000U );
    char* buffer = (char*) malloc(64000U);
    // buffer is a work buffer for a 320x200x256 virtual screen.
    ... // fill in buffer
    // Now copy the buffer to video memory.
    #ifdef __FLAT__
    copy32aux( 0, sel, (unsigned long)buffer, _DS, 64000L );
    #else
    _fmemcpy( MK_FP(sel,0), buffer, 64000U );
    #endif
    ...

unsigned short DPMIFAR dpmi_getSelectorInc(void);

  Returns a value that can be added to a selector to access it's next
  congruent selector. Usually used with the selector returned by
  dpmi_allocateSelector(<value greater than 1>) to calculate subsequent
  congruent selectors.

LinearAddress DPMIFAR dpmi_getSelectorLinearAddress( PMSelector sel );

  On success, returns the linear base address for a selector; on failure
  returns 0.

int DPMIFAR dpmi_setSelectorLinearAddress( PMSelector sel, LinearAddress addr );

  Use this function to set the linear base address for a selector. On success
  returns 1; on failure returns 0.

unsigned long DPMIFAR dpmi_getSelectorLimit( PMSelector sel );

  Returns the limit for a selector on success, else 0 on failure.

int DPMIFAR dpmi_setSelectorLimit( PMSelector sel, unsigned long len );

  Use this function to set the limit for a selector. On success returns 1; on
  failure returns 0.

PMRights DPMIFAR dpmi_getSelectorRights( PMSelector sel );

  Returns the access rights for a selector on success, else 0 on failure.

int DPMIFAR dpmi_setSelectorRights( PMSelector sel, PMRights rights );

  Use this function to set the access rights for a selector. Returns 1 on
  success, or 0 on failure.

PMSelector DPMIFAR dpmi_aliasCodeToData( PMSelector codeSel );

  Use this function to allocate a selector that has "normal" data access
  rights, but the same base linear address and limit of the given code
  selector. Returns the selector on success, or 0 on failure. Use
  dpmi_freeSelector() to free the selector when you're done with it.

int DPMIFAR dpmi_getDescriptor( PMSelector sel, LPDescriptor buf );

  Use this function to get the descriptor for a selector. Returns 1 on success,
  or 0 on failure.

int DPMIFAR dpmi_setDescriptor( PMSelector sel, LPCDescriptor buf );

  Use this function to set the descriptor for a selector. Returns 1 on success,
  or 0 on failure.

int DPMIFAR dpmi_allocateSpecificSelector( PMSelector sel );

  Use this function to allocate a specific selector for which you already know
  the value. Returns 1 on success, or 0 on failure. Use dpmi_freeSelector() to
  free the selector when you're done with it.

DOS Memory Management Services
------------------------------

  These functions are used to allocate, reallocate, or free memory accessible
  in the DOS real mode memory range (0-640KB). Since this memory is accessible
  to both real and protected mode code, they are useful for interprocess
  buffers.

DosMemory DPMIFAR dpmi_allocateDosMem( unsigned long len );

  Allocate dos memory of <len> bytes. Returns a DosMemory object (containing
  both a RMSegment and a PMSelector) on success, or 0 on failure. Example:
    typedef struct tagECB {...} ECB;  // Used for Netware functions.
    DosMemory mem;
    PMSelector sel;
    ECB DPMIFAR* ecbPtr;

    mem = dpmi_allocateDosMem( sizeof(ECB) );
    sel = DM_SELECTOR(mem);
    #ifdef __FLAT__
    ecbPtr.offset = 0;    ecbPtr.selector = sel;
    ...
    #else
    ecbPtr = (ECB DPMIFAR*) MK_FP(sel, 0);
    ...
    #endif
    // You now have a ECB pointer that can be used with the real mode Netware
    // functions! For Netware, use the real mode address of the memory; for
    // example:
    //   RMAddress addr;
    //   addr.offset = 0;
    //   addr.segment = DM_SEGMENT(mem);
    //   ...
    // In your protected mode program, juse access it with ecbPtr.
  When you're done with this dos memory, use dpmi_freeDosMem() to free it. For
  example:
    ...
    dpmi_freeDosMem( DM_SELECTOR(mem) );
    ...
  Note that for both the segment and the selector, the offset to this buffer
  is 0.

int DPMIFAR dpmi_freeDosMem( PMSelector sel );

  Use this function to free memory allocated with dpmi_allocateDosMem(). Returns
  1 on success, or 0 on failure.

int DPMIFAR dpmi_reallocateDosMem( PMSelector sel, unsigned long len );

  Use this function to reallocate dos memory that was allocated with the
  function dpmi_allocateDosMem(). Returns 1 on success, or 0 on failure.

Interrupt Services
------------------

  These functions are used to get and set vectors for real mode interrupts,
  protected mode interrupts, and protected mode exceptions.

RMAddress DPMIFAR dpmi_getRMVect( unsigned char intNo );

  Use this function to get the real mode address of a real mode interrupt
  vector. Returns the vector on success, or 0 on failure.

int DPMIFAR dpmi_setRMVect( unsigned char intNo, RMAddress addr );

  Use this function to set a real mode vector for a real mode interrupt. Returns
  1 on success, or 0 on failure.

PMFarAddress DPMIFAR dpmi_getPMVect( unsigned char intNo );

  Use this function to get the protected mode address of a protected mode
  interrupt vector. Returns the vector on success, or 0 on failure. Note that
  with DPMI32, you are returning a struct - the implementation for doing this
  varies between compiler manufacturers (this code is for BC++ 4.5).

int DPMIFAR dpmi_setPMVect( unsigned char intNo, PMFarAddress addr );

  Use this function to set the protected mode address of a protected mode
  interrupt vector. Returns 1 on success, or 0 on failure.

PMFarAddress DPMIFAR dpmi_getPMHandler( unsigned char faultNo );

  Use this function to get the address of an exception handler. Returns the
  address on success, or 0 on failure. Note that with DPMI32 you are returning
  a struct - the implementation for doing this varies between compiler
  manufacturers (this code is for BC++ 4.5).

int DPMIFAR dpmi_setPMHandler( unsigned char faultNo, PMFarAddress addr );

  Use this function to set the vector for an exception handler. Returns 1 on
  success, or 0 on failure.

Translation Services
--------------------

  These functions allow you to interact with real mode procedures, and also
  retrieve DPMI host version information (to tell if something is supported).

int DPMIFAR dpmi_intr( unsigned char intNo, LPPMRegs regs );

  Use this function to call a real mode interrupt. <regs> should contain the
  desired values for the registers on entry to the interrupt. Returns 1 on
  success, or 0 on failure. If successful, <regs> will contain the values of
  the CPU registers when the interrupt returned. Note that the <regs> cs and
  ip members are ignored by this call, and that on return ss, sp, cs and ip
  are NOT the ones returned by the interrupt. If <regs> ss and sp are 0, then
  the DPMI host will provide a real mode stack, but this stack will only hold
  30 words (60 bytes)!
  Note too that you will be entering real mode code. This means that the <regs>
  members that represent segment registers MUST contain RMSegment values, and
  not PMSelector values (if they are used by the interrupt). A good practice
  is to use dpmi_allocateDosMem() to allocate any buffers that the interrupt
  needs to access (and use DM_SEGMENT() to get the RMSegment for the buffer),
  and to set any unused segment registers to 0. For example:
    PMRegs regs;
    DosMemory mem;
    ...
    memset( &regs, 0, sizeof(PMRegs) ); // zero unused registers
    mem = dpmi_allocateDosMem( 1024 );  // we want a 1K stack!
    regs.h.ss = DP_SEGMENT(mem);
    regs.h.sp = 1020;            // don't point at *bottom* of stack!
    ...
    dpmi_intr( 0x21, &regs );
    dpmi_freeDosMem( DM_SELECTOR(mem) );
    ...

int DPMIFAR dpmi_callFar( RMAddress addr, LPPMRegs regs );

  Use this function to call a real mode procedure. <regs> should contain the
  desired values for the registers on entry to the function, and the function
  MUST return with a RETF call (be a far function). For more information on
  <regs>, see dpmi_intr().

int DPMIFAR dpmi_callIntr( RMAddress addr, LPPMRegs regs );

  This function is just like dpmi_callFar(), but the difference is that
  procedure you're calling returns with an IRET instruction instead of a
  RETF instruction.

RMAddress DPMIFAR dpmi_allocateCallBack( PMFarAddress proc, LPPMRegs regs );

  Use this function to allocate a "thunk" which real mode code can call to
  get to a protected mode procedure in <proc>. The thunk will save all the
  current (real mode) register values in <regs>, switch into protected mode,
  call <proc>, switch back to real mode, restore the registers from the
  values in <regs>, and return (with a RETF). This allows real mode code to
  call protected mode code, such as when you're registering a mouse handler
  callback, etc.
  <proc> isn't in a good form for a C function. On entry, <proc> has the
  following register values:
      DS:SI is the selector:offset of the real mode stack (DS:ESI in DPMI32).
      ES:DI is the selector:offset of <regs> (ES:EDI in DPMI32).
      SS:SP is a locked protected mode stack provided by the host (SS:ESP).
      All other registers are undefined.
  When <proc> returns it must:
      Return with an IRET call, not a RET or RETF!
      ES:(E)DI is the selector:offset of a PMRegs object
  You have to watch out for reentrance, since you've only got one <regs> object
  for this callback. If the callback takes a while, you'll want to copy the
  <regs> struct (in ES:(E)DI) to an allocated buffer and re-enable interrupts.

int DPMIFAR dpmi_freeCallBack( RMAddress addr );

  Use this function to free a callback allocated with dpmi_allocateCallBack().
  Returns 1 on success, or 0 on failure.

PMFarAddress DPMIFAR dpmi_getPMSaveStateProc(void);

  Use this function to obtain a protected mode function pointer which, when
  called, will save the protected mode state of the processor. On entry to
  the function, you need to set the registers to:
      ES:DI points to a protected mode buffer (ES:EDI for DPMI32).
      AL = 0 to save the state, or 1 to restore the state.
  Use dpmi_getSaveStateSize() to get the required size of the buffer.
  Returns the address of the function on success, or 0 on failure.

RMAddress DPMIFAR dpmi_getRMSaveStateProc(void);

  Use this function to obtain a real mode function pointer which, when called,
  will save the real mode state of the processor. On entry to the function, you
  need to set the registers to:
      ES:DI points to a real mode buffer (ES:EDI for DPMI32).
      AL = 0 to save the state, or 1 to restore the state.
  Use dpmi_getSaveStateSize() to get the required size of the buffer.
  Returns the real mode address of the function on success, or 0 on failure.

unsigned short DPMIFAR dpmi_getSaveStateSize(void);

  Use this function to obtain the size of the buffer required for a call to
  the Save State procedure(s) (see dpmi_getPMSaveStateProc() and
  dpmi_getRMSaveStateProc()). Note that a return value of 0 IS valid - it means
  that the DPMI host doesn't require that the state be saved.

PMFarAddress DPMIFAR dpmi_getPMSwitchProc(void);

  Use this function to obtain a protected mode function pointer which, when
  called, will switch to real mode. Returns the function pointer on success,
  or 0 on failure.
  On entry to the function, the registers need to be:
      AX = new DS (RMSegment)
      CX = new ES (RMSegment)
      DX = new SS (RMSegment)
      BX = new SP (EBX = new ESP on DPMI32)
      SI = new CS (RMSegment)
      DI = new IP (EDI = new EIP on DPMI32)
  It is up to you to save the state before calling this function pointer (see
  dpmi_getPMSaveStateProc()).

RMAddress DPMIFAR dpmi_getRMSwitchProc(void);

  Use this function to obtain a real mode function pointer which, when called,
  will switch to protected mode. Returns the function pointer on success,
  or 0 on failure.
  On entry to the function, the registers need to be:
      AX = new DS (PMSelector)
      CX = new ES (PMSelector)
      DX = new SS (PMSelector)
      BX = new SP (EBX = new ESP on DPMI32)
      SI = new CS (PMSelector)
      DI = new IP (EDI = new EIP on DPMI32)
  It is up to you to save the state before calling this function pointer (see
  dpmi_getRMSaveStateProc()).

void DPMIFAR dpmi_getVersion( LPDPMIVersion ver );

  Use this function to get version information for the DPMI host.

Memory Management Services
--------------------------

  These functions perform actions on linear memory.

int DPMIFAR dpmi_getMemInfo( LPDPMIMemInfo info );

  Use this function to obtain "heap" information from the DPMI host. Returns
  1 on success, or 0 on failure.

PMHandle DPMIFAR dpmi_allocateLinearMem( unsigned long len,
                                         LinearAddress DPMIFAR*addr );

  Use this function to allocate linear memory of length <len>. If successful,
  returns a handle to the memory, and *<addr> is set to the linear address of
  the memory. On failure, returns 0.

int DPMIFAR dpmi_freeLinearMem( PMHandle handle );

  Use this function to free memory allocated with dpmi_allocateLinearMem().
  Returns 1 on success, or 0 on failure.

PMHandle DPMIFAR dpmi_reallocateLinearMem( PMHandle handle, unsigned long len,
                                           LPRMAddress addr );

  Use this function to reallocate memory allocated with
  dpmi_allocateLinearMem() to <len> bytes. On success, returns the new handle
  for the memory and *<addr> is set to the new linear address; on failure
  returns 0.

Page Locking Services
---------------------

  Use these functions (cautiously) to effect page locking.

int DPMIFAR dpmi_lockLinearMem( LinearAddress addr, unsigned long len );

  Use this function to lock a linear memory range starting at <addr> and
  extending for <len> bytes. Returns 1 on success, or 0 on failure. Note that
  in implementation, pages get locked, so that more than <len> bytes might
  get locked if it straddles a page boundary.

int DPMIFAR dpmi_unlockLinearMem( LinearAddress addr, unsigned long len );

  Use this function to unlock memory locked with dpmi_lockLinearMem(). Returns
  1 on success, or 0 on failure.

int DPMIFAR dpmi_markRModePageable( RMAddress addr, unsigned long len );

  Use this function to mark a section of real mode memory as pageable. Returns
  1 on success, or 0 on failure.

int DPMIFAR dpmi_relockRModeRegion( RMAddress addr, unsigned long len );

  Use this function to lock a section of real mode memory that was marked as
  pageable with dpmi_markRModePageable(). Returns 1 on success, or 0 on failure.

unsigned long DPMIFAR dpmi_getPageSize();

  Use this function to obtain the number of bytes per page. Returns the value
  on success, or 0 on failure.

Demand Paging Performance Tuning Services
-----------------------------------------

  These functions are used to mark memory as LRU (so its first to be paged out
  of memory), or to discard memory. Use with caution.

int DPMIFAR dpmi_markLinearMem( LinearAddress addr, unsigned long len );

  Use this function to mark a linear range as next to be paged. Returns 1 on
  success, or 0 on failure.

int DPMIFAR dpmi_discardLinearMem( LinearAddress addr, unsigned long len );

  Use this function to request that a linear range be discarded. Returns 1 on
  success, or 0 on failure.

Physical Address Mapping
------------------------

  This function (not always supported, unfortunately) is for mapping physical
  memory addresses to a linear address for use with other dpmi_xxxx() functions.
  Normally, this would be used with a peripheral that maps to a (high) physical
  memory address.

LinearAddress DPMIFAR dpmi_mapPhysicalMem( LinearAddress addr,
                                           unsigned long len );

  Use this function to map a physical memory range (above 1MB!) to a linear
  memory range that can be used with other dpmi_xxxx() functions. Returns the
  mapped linear address on success, or 0 on failure.

Virtual Interrupt State Services
--------------------------------

  These functions effect the virtual interrupt table.

int DPMIFAR dpmi_disableIntr(void);

  Use this function to disable interrupts. Returns 1 if interrupts were
  previously enabled, or 0 if they were disabled.

int DPMIFAR dpmi_enableIntr(void);

  Use this function to enable interrupts. Returns 1 if interrupts were
  previously enabled, or 0 if they were disabled.

int DPMIFAR dpmi_getIntr(void);

  Use this function to get the virtual interrupt state. Returns 1 if interrupts
  are enabled, or 0 if they are disabled.

Debug Register Support
----------------------

  These function allow rudimentary debugging capabilities.

WatchPoint DPMIFAR dpmi_setWatchPoint( LinearAddress addr, int nrBytes,
                                       int type );

  Use this function to set a watch point on a byte, word, or double word in
  linear address space. <addr> is the linear address of the memory, <nrBytes>
  is 1 (a byte), 2 (a word), or 4 (a double word), and <type> is the type of
  watch point to apply (must be one of watchpoint_types). Returns the watch
  point "handle" on success, or 0 on failure.

int DPMIFAR dpmi_clearWatchPoint( WatchPoint );

  Use this function to "clear" a watch point that was applied with the
  dpmi_setWatchPoint() function. Returns 1 on success, or 0 on failure.

int DPMIFAR dpmi_watchPointExecuted( WatchPoint );

  Use this function to test if a watch point was executed (read, written, etc).
  Returns 1 if the watch point was executed, or 0 on failure or if the watch
  point was not executed.

int DPMIFAR dpmi_resetWatchPoint( WatchPoint );

  Use this function to reset a watch point so you can again test for when it
  becomes executed. Returns 1 on success, or 0 on failure.

Goodies
-------

  Currently, these functions are just DMI32-only functions that will read
  and write to a PMFarAddress (or selector+offset combination) in the same
  manner as a _fmemcpy() function call. You must be compiling for DPMI32
  for these functions to be defined, and they are only present in DPMIC32.LIB.

void copy32( PMFarAddress dest, PMFarAddress source, unsigned long len );

  Use this function to write <len> bytes from source to dest.

void copy32aux( unsigned long destOfs, PMSelector destSel, unsigned long srcOfs,
                PMSelector srcSel, unsigned long len );

  Use this function to write <len> bytes from srcSel:srcOfs to destSel:destOfs,
  where srcSel and destSel are protected mode selectors, and srcOfs and destOfs
  are 32-bit offsets into those selectors.

