CPMI Overview
===========================================================================

The CA-Clipper community has been blessed in recent months with the ability
to create Clipper applications that run in protected mode.  There are
currently three different products (and who knows, maybe more to come)
that alleviate many memory problems associated with Clipper programming and
have brought relief to long-suffering developers.

As with all silver linings, however, this one has a dark cloud <g>.  Many
third-party libraries, as well as some home-grown C / ASM code, will not
work with these products because they break protected mode programming
rules (no one's fault, really -- the DOS extender products are very new and
prior to their release no one expected Clipper apps to ever run in anything
but real mode).  To their credit, the vendors of the DOS extender products
have attempted to minimize the problem by providing APIs that can be used
to make C or assembler code compatible with protected mode.  Unfortunately,
each vendor uses its own set of API functions, so a function written for
one API will not work when used with a different DOS extender.

The Clipper Protected Mode Interface (hereafter called CPMI) is an
attempt to solve this problem.  It provides a set of generic functions
that will work regardless of which DOS extender is used.  This is made
possible by the fact that all the existing extender products support the
use of the DOS Protected Mode Interface (DPMI) specification, even when
the application is not running under a DPMI host.  The functions in the
CPMI API use DPMI calls to achieve compatibility with any DOS extender
that supports the DPMI protocol.

Another attractive feature is that the CPMI is Public Domain, so it may
be freely distributed by third-party developers and anyone else who needs
its services.  It is hoped that this will simplify compliance with
protected mode rules since it is no longer necessary to support each
vendor's API.



Programming in protected mode
===========================================================================

Although coding for protected mode is a new experience for most of us, it
is not difficult and any code will run fine as long as a few simple rules
are followed.  Failure to follow the rules will result in a General
Protection Fault (GPF), which is actually nice in a way because it makes
finding memory-related bugs much easier.  Adhere to the following rules
and you should be able to avoid GPFs:

1.  A selector value (referred to as a segment value in real mode) loaded
    into a segment register actually maps to a chunk of memory that may
    be located anywhere within the physical address space.  With extenders
    that support virtual memory, the selector can even refer to memory
    that has been swapped to disk.

    a) Never load a segment register with junk values or a GPF will
       probably occur.  Every time a segment register is loaded with a
       value the microprocessor checks to make sure it is a valid selector
       and generates a GP fault if it is not.

    b) Never perform segment arithmetic on a selector value.  This is done
       in real mode code to either normalize a pointer, force segments
       to be aligned on a specific boundary, or to access data that is
       greater than 64K in size.  A selector value is a unique ID which has
       no relationship to a specific physical memory location.

    c) All selectors have a size associated with them and attempts to
       access data at an offset greater than that size will result in a
       GPF.  This is one of the greatest strengths of a DOS Extender
       because most obscure bugs in real mode code occur when a wild
       pointer is used.  This will usually cause a GPF in protected mode
       because either the selector or offset value will be invalid.

    d) Segment registers can be loaded with a selector value of 0 (a NULL
       selector), but never try to write to or read from a NULL selector.

    e) Segment values in real mode always point to a physical address equal
       to the segment value multiplied by 16, but selector values in
       protected mode may point to anywhere in memory.  The CPMI contains
       services for obtaining protected mode selectors from real mode
       addresses.

2) You cannot execute code in a data segment and you cannot write to data
   in the code segment.

3) You may not directly issue real mode interrupts from protected mode.
   The CPMI has services for calling real mode interrupts if you find it
   necessary.



The CPMI.H header file
===========================================================================

Prototypes for all of the CPMI functions can be found in the CPMI.H header
file, along with a few typedefs used by by the CPMI.  In addition, each
.ASM source file contains a detailed function header that describes syntax
and return value information.



Writing Toolkit functions for protected mode
===========================================================================

Most Toolkit functions will work fine in protected mode with no
modifications.  In some cases, however, the function may need to be altered
to achieve protected mode compatibility.  To prevent a version-control
nightmare, all Toolkit functions should be written such that the SAME CODE
will work in both real and protected mode -- i.e. there SHOULD NOT be two
separate versions of the same function.

This is much easier than it might appear at first glance.  For example,
consider the following function from version 2.0 of the Toolkit:

        CLIPPER FT_Alt( void )
        {
           _retl( ( int ) ( ( * ( char * ) 0x00000417 ) & 0x8 ) );

           return;
        }

This function returns the state of the ALT key, and it works just fine
in real mode.  In protected mode, however, it causes a GPF because a null
selector is involved.  It can easily be fixed, though, by using the
address 0040:0017 instead of 0000:0417.  The two addresses point to the
exact same physical memory in real mode, and in protected mode the DOS
extender provides a selector of 40h that corresponds to the real mode
segment 40h.

It won't always be so simple, of course.  In some cases it may be
necessary to construct the code such that it works slightly differently
depending on the CPU mode.  For example, suppose you wanted to retrieve
the BIOS date stamp at F000:FFF5.  Making it compatible with both real and
protected mode isn't hard when you use the CPMI services:

        #define DATEPTR ( ( char * ) 0xF000FFF5 ) )

        CLIPPER BIOSDate( void )
        {
           auto int isProtMode = cpmiIsProtected();
           auto char * cDate;

           if ( isProtMode )
           {
              FP_SEG( cDate ) = cpmiProtectedPtr( DATEPTR, 8 );
              FP_OFF( cDate ) = 0;
           }
           else
              cDate = DATEPTR;

           _retclen( cDate, 8 );

           if ( isProtMode ) cpmiFreeSelector( FP_SEG( cDate ) );

           return;
        }

Given the ease with which it can be done, all Toolkit functions are
required to work in either real or protected mode.  The CPMI API exists to
help you accomplish this goal.  If you require further assistance, please
contact the Toolkit librarian or the CPMI author.