[<<Previous Entry]
[^^Up^^]
[Next Entry>>]
[Menu]
[About The Guide]
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.
This page created by ng2html v1.05, the Norton guide to HTML conversion utility.
Written by Dave Pearson