home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Monster Media 1993 #2
/
Image.iso
/
clipper
/
exoapi.zip
/
EXOAPI.TXT
< prev
next >
Wrap
Text File
|
1993-06-15
|
44KB
|
1,042 lines
ExoSpace version 1.0 API (revision #1)
--------------------------------------
ExoSpace(tm) is based on the Rational DOS/16M(tm) 286 DOS Extender. As a
286 DOS extender it executes 99% of the code written for real mode
applications in protected mode. In fact, as long as a few protected mode
rules are followed converting an application to protected mode can be
relatively easy. Unfortunately, programmers who have no experience writing
protected mode applications routinely break one of more of these rules and
breaking just one rule in one location within a program can make the
program unusable in protected mode.
The purpose of this document is to review protected mode rules, to
demonstrate where protected mode programming is radically different from
real mode programming, and to define the API functions available from
ExoSpace that may be necessary to your C or Assembler functions.
When a program breaks one or more of the protected mode rules, a General
Protection Fault or GP Fault occurs. This causes ExoSpace to print out a
message and abort the program back to DOS. In truth, GP Faults are a
terrific debugging tool. They either show where you are breaking
protected mode programming rules or they highlight bugs that would
normally trash memory in real mode.
The quickest way to try to convert an application or third party library to
protected mode is to link the application with ExoSpace creating a .MAP
file with the "MAP A" command and then simply running the application to
see if it works. If it generates GP faults then you can look at the map
file to see what module or function generated the fault. You can then look
at that module or function to see what caused the GP Fault. In most cases
you can narrow down any problems using this method.
It is also best to begin the process of conversion under plain DOS using a
VCPI or XMS extended memory manager. Do not begin by running in a Windows
DOS box or under any other memory manager that only emulates the DPMI
protocol. This is because the DPMI protocol is much more restricting.
When your program is not running under DPMI, it can expect the most used
selector values to point to the same memory they do in real mode. For
example, when not under DPMI you can expect selectors 0xB000 and 0xB800 to
point to video memory. Once you have the application working under a
non-DPMI manager you can then begin the process of making it compatible
with DPMI by using the ExoProtectedPtr() function to create protected mode
pointers to real mode addresses.
We also have a protected mode debugger available for those who have a
great deal of work to do in making their code compatible with protected
mode. The debugger will halt the program on a GP fault allowing you to
see exactly which instruction caused the problem. If you are having a
great deal of trouble tracking down problems in your code, please contact
us about the debugger.
We also suggest that you take some time to read a few books about
protected mode programming with DOS extenders. Your current code may be
easy to convert to protected mode (some code may require absolutely no
changes, especially C code), but protected mode programming is the future
direction DOS will take and it may be useful to be more familiar with it.
The book we found helpful was:
Extending DOS
Ray Duncan
Addison Wesley Publishing
ISBN 0-201-56798-9
(The first edition uses the Rational DOS/16M extender and the second
edition uses the PharLap 286 DOS extender.)
Protected Mode Rules
--------------------
1) A selector value (referred to as a segment value in real mode) loaded
into DS, ES, SS, and CS actually references a "chunk" of memory that may
occur anywhere within the physical address space and can even be moved
around by the operating system. Therefore, when dealing with selector
values:
a) Never load DS or ES with "junk" values or a GP fault may occur. Never
use DS or ES as "scratch" registers that can be loaded with arbitrary
values. Everytime DS and ES is loaded with a value the microprocessor
checks to make sure it is a valid selector value 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 or to access data that is
greater than 64K in size. A selector value is a unique ID which has no
relationship to the data it points to.
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 GP
fault. This is one of the greatest strengths of a 286 DOS Extender because
most obscure bugs in real mode code occur when a "wild pointer" is used.
This will usually cause a GP fault in protected mode because either the
selector or offset value will be invalid.
d) DS and ES can be loaded with a selector value of 0 (a NULL pointer in C
parlance), but never try to write to or read from a NULL pointer. A great
deal of C code has this mistake in it. Attempts to read from or write to a
NULL pointer will generate a GP fault.
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. Therefore, you cannot assume standard
addresses such as 0xB000 and 0xB800 (used to address a monochrome or color
screen) will point to the same memory they do in protected mode. In truth,
under DPMI only selector 0x40 is guaranteed to point to the BIOS data area
- no other selector value is predefined. For maximum portability you will
have to use the ExoProtectedPtr() function to address absolute physical
addresses.
2) You cannot execute code in a data segment and you cannot write to data
in the code segment.
3) ExoSpace handles most of the standard DOS interrupts transparently, but
when passing pointers to buffers to software interrupt calls not handled
transparently by ExoSpace, the buffers must exist in low DOS memory and
the pointers must be converted from protected mode selector:offset pairs
to real mode segment:offset pairs.
ExoSpace errata
---------------
Although ExoSpace does provide a DOS protected mode version of the
int86/intdos() functions, they cannot be used when calling a real mode
interrupt which returns a value in DS or ES. Real mode segment values
stored in DS or ES are invalid in protected mode. When a real mode
interrupt modifies DS or ES in an int86/intdos() call, ExoSpace will most
likely replace the value with a 0 to prevent a GP fault. Use of
ExoRMInterrupt() is always preferred because it should be slightly faster
than the int86/intdos() functions and will return the real mode values of
DS and ES.
In 286 protected mode, the IN and OUT instructions work the same as they
do in real mode. No special handling of the I/O ports in necessary.
Under ExoSpace, the PSP is the same as it is in real mode. It is located
in low DOS memory so both DOS and ExoSpace have access to it. All of the
pointer values are real mode values except for the segment address of the
environment at offset 0x2C.
The DOS16M environment variable can be used to restrict the amount of
memory ExoSpace uses. This can be used to test your applications with
various amounts of memory. For example, to test your application under a
2MB configuration use "SET DOS16M=:2MB" (be sure to use the :).
Interrupts
----------
Many interrupts are handled "transparently" by ExoSpace especially the DOS
function interrupt 0x21. Interrupts not handled transparently by ExoSpace
can safely be called as long as they don't pass pointers to buffers or
return pointers to data. A brief breakdown of some of the important
interrupts follows:
Interrupt 0x08 (System Clock): If you install a handler with interrupt
0x21, function 0x25, ExoSpace will automatically call your interrupt
handler in protected mode. Interrupt 0x1C handlers are NOT called.
Interrupt 0x1C (User Timer Tick): Install a handler for interrupt 8
instead.
Interrupt 0x0B-0C (Serial port interrupts): If a handler is installed with
interrupt 0x21, function 0x25, ExoSpace will automatically call your
interrupt handler. If you are doing high-speed interrupt-driven
communications you will need to install bi-modal interrupt functions.
Interrupt 0x10 (Video BIOS): Because the majority of video BIOS interrupts
do not pass pointers to buffers, the majority of them work just fine with
ExoSpace. Of those functions which do pass pointers to buffers, the
following are handled transparently by ExoSpace WITHOUT the INT10 package:
function 0x10, subfunctions 0x02, 0x09; function 0x13; function 0x1B;
function 0x1C, subfunctions 0x01, 0x02.
Interrupt 0x16 (Keyboard): These interrupts are handled transparently by
ExoSpace, but function 0x01 and 0x11 are handled specially. Because these
interrupts are resignaled in real mode and because doing a mode switch
takes time, these interrupts are only signaled in real mode every timer
tick (1/18th of a second) or after a call to interrupt 0x16, functions 0x00
or 0x10. This optimization helps performance.
Interrupt 0x21 (DOS): Most of the documented interrupts are fully
supported. Functions that require special notes are:
0x1F (Get DPB) - You must use ExoProtectedPtr() to translate the real
mode address returned in DS:BX by this function.
0x26 (Create PSP) - Pass return value from ExoRealPtr().
0x2A (Get date) - The real mode interrupt is resignaled only once every
timer tick to help performance.
0x2C (Get time) - The real mode interrupt is resignaled only once every
timer tick to help performance.
0x44 (IOCTL) - subfunction 0x0C, minor codes 0x45 and 0x65; and
subfunction 0x0D, minor codes 0x40 - 0x42, 0x60 - 0x62 are not
translated. Use ExoRealPtr() to translate any addresses passed to
the functions, use ExoRMInterrupt() to call the functions, and use
ExoProtectedPtr() to translate any return addresses.
0x48 (Allocate Memory) - Memory is most likely allocated from extended
extended memory and the returned value in AX is a protected mode
selector. It is recommended that you use Clipper's memory
allocation functions.
0x50 to 0x53, 0x55, 0x5C, 0x5D - These functions are not transparently
supported. Use ExoRealPtr() to translate any addresses passed to
the functions, use ExoRMInterrupt() to call the functions, and use
ExoProtectedPtr() to translate any return addresses.
Interrupts 0x25 and 0x26 (Absolute Disk Read/Write): These interrupts ARE
NOT handled transparently by ExoSpace. To use these interrupts you MUST
link in the DOS25 package. When this package is linked in, the interrupts
are handled transparently.
Interrupt 0x31 (DPMI): When running under a DPMI host, all DPMI calls are
passed on to the host. When the program is not running under a DPMI host,
ExoSpace provides a handler for some of the most common DPMI calls: 0 - 3,
6 - C, 100 - 102, 200, 201, 204, 205, 300 - 304, 400, 500 - 503, 800, 801,
900 - 902 (all values are in hex).
Interrupt 0x33 (Mouse): This interrupt is NOT handled transparently by
ExoSpace. To use this interrupt you MUST link in the MOUSE package. When
the MOUSE package is linked in, all functions are supported except for
functions 0x18, 0x19, 0x1F, 0x20.
Interrupt 0x5C (NetBIOS): This interrupt is NOT handled transparently by
ExoSpace. To use this interrupt you MUST link in the NET5C package. When
the NET5C package is linked in, all functions are supported except for
functions 0x71 and 0x72.
Interrupt 0x7A (IPX/SPX): This interrupt is NOT handled transparently by
ExoSpace. To use this interrupt you MUST link in the IPX package. When
the IPX package is linked in, all functions are supported.
Interrupt 0x7F (8514/A): This interrupt is NOT handled transparently by
ExoSpace. To use this interrupt you MUST link in the 8514 package. When
the 8514 package is linked in, all functions are supported.
Interrupts 0xF0 - 0xFF: These interrupts are reserved for use by ExoSpace.
Packages
--------
ExoSpace packages contain additional support for interrupts which pass
pointers to buffers. The packages and what they support are listed below.
INT10 - This package provides support for the video BIOS interrupts 0x10,
0x11, and 0x12. Function 0x10, subfunctions 0x12 and 0x17 are supported.
Function 0x11, subfunctions 0x00, 0x10, 0x20, 0x21 are supported with the
warning that the support is not fully tested. Function 0x12, subfunctions
0x00, 0x02, and 0x03 are supported with the warning that the support is not
fully tested. Function 0x5F, subfunctions 0x91 and 0x92 are supported.
Functions 0xF2 to 0xF5 and 0xF7 (the Microsoft mouse EGA support functions)
are supported.
DOS25 - This package provides support for the absolute disk read/write
interrupts 0x25 and 0x26. Attempts to call these interrupts in an
ExoSpace'd application without this package linked in will result in
undesirable behavior.
MOUSE - This package provides support for the Microsoft mouse interrupt
0x33 functions except for functions 0x18, 0x19, 0x1F, 0x20. Attempts to
call many of the mouse functions in an ExoSpace'd application without this
package linked in will result in undesirable behavior.
NET5C - This package provides support for the NetBIOS interrupt 0x5C
functions except for functions 0x71 and 0x72. Attempts to call the NetBIOS
functions in an ExoSpace'd application without this package linked in will
result in undesirable behavior.
IPX - This package provides support for IPX/SPX. Attempts to call the IPX
functions in an ExoSpace'd application without this package linked in will
result in undesirable behavior. There has been one report of a problem
with this package. We are interested in hearing from anyone using this
package.
8514 - This package provides support for the IBM 8514 Display Adapter.
Attempts to call the 8514 functions in an ExoSpace'd application without
this package linked in will result in undesirable behavior.
NOVM - This is not a package, but a switch to tell ExoSpace not to link in
the ExoSpace VM system which uses the Rational DOS/16M VMM system. This
switch is currently necessary to create executables that can be debugged
with Rational's protected mode debugger. Otherwise, this switch is not
currently recommended because it not only disables code and data swapping,
but it also inhibits memory compaction.
API Library Functions
---------------------
The ExoSpace API library functions are all C callable functions and are
documented below as C prototypes. The Rational DOS/16M 286 DOS Extender
has a rich set of API functions and we have chosen to emulate a small
subset of the most useful functions in our API. This API should satisfy 9
out of 10 developers doing C or Assembler programming.
We will probably be providing a real mode "stub" library in the next few
weeks for those who wish to try to create a "bimodal" library that works
with and without ExoSpace. This "stub" library will contain dummy ExoSpace
API functions which will return values expected for non-ExoSpace'd real
mode code. You will be able to freely add this library to your own
library. Thus, when linking with ExoSpace the ExoSpace API functions will
be linked in and when using a real mode linker such as Blinker or RTLink
the "stub" functions will be linked in from your library.
/* definition needed for ExoRMInterrupt() below */
typedef struct _exoregs {
unsigned ds, es;
unsigned di, si, bp, sp; /* note: sp is ignored */
unsigned bx, dx, cx, ax;
} EXOREGS;
int ExoFreeSelector(unsigned int selector);
int ExoIsDPMI(void);
int ExoIsExoSpace(void);
int ExoIsPM(void);
int ExoIsVMM(void);
void *ExoProtectedPtr(void *rmptr, unsigned int sizebytes);
void *ExoRealPtr(void *pmptr);
int ExoReside(void *pmptr);
int ExoRMInterrupt(int, EXOREGS *inregs, EXOREGS *outregs);
void (*ExoSegCSAlias(void *pmptr))();
void *ExoSegDSAlias(void *pmptr());
void *_xalloclow(unsigned int sizebytes);
void _xfreelow(void *lowmemory);
ExoFreeSelector()
Purpose: Cancels a protected mode segment descriptor created by
ExoProtectedPtr(), ExoSegCSAlias(), or ExoSegDSAlias().
Syntax: int ExoFreeSelector(unsigned int selector);
Returns: Always returns 0.
See also: ExoProtectedPtr(), ExoSegCSAlias(), or ExoSegDSAlias()
Notes: This function is a primitive function; it corresponds to the DPMI
function that cancels a descriptor.
This function must be passed only the selector part of a pointer
created with ExoProtectedPtr(), ExoSegCSAlias(), or
ExoSegDSAlias().
Because selectors are a limited resource in protected mode (there
are "usually" around 8000 available for code and data segments),
you should free selectors you have created with
ExoProtectedPtr(), ExoSegCSAlias(), or ExoSegDSAlias() as soon as
you are done with them.
We had intended to include the API function ExoSegCancel() which
would accept a pointer instead of just the selector portion of a
pointer, but it was inadvertently left out. A future revision of
the API will include this function.
Example: This code creates a protected mode pointer to video color memory,
does some unknown operation with it and then frees the protected
mode selector.
void *rmvideo;
void *pmvideo;
/* set up pointer to point to real mode address 0xB800:0 which
is the address of the color video card */
FP_SEG(rmvideo) = 0xB800;
FP_OFF(rmvideo) = 0;
/* create protected mode pointer for use in protected mode */
pmvideo = ExoProtectedPtr(rmvideo, 0x8000);
... more code using pmvideo pointer would normally be here ...
/* free up selector used by pmpointer now that we are done
with it */
ExoFreeSelector(FP_SEG(pmvideo));
ExoIsDPMI()
Purpose: Returns true if running in a DPMI host environment.
Syntax: int ExoIsDPMI(void);
Returns: This function returns nonzero if the program is running in a DPMI
host. If the program is not running under DPMI, ExoIsDPMI returns
zero.
See also: ExoIsExoSpace(), ExoIsPM()
Notes: Use this function to determine whether your program is running
under a DPMI host, such as Windows 3.x in enhanced mode.
Example: This program fragment prints a message telling whether or not the
program executing is running under DPMI.
if (ExoIsDPMI())
printf("Program is running under DPMI");
else
printf("Program is not running under DPMI");
ExoIsExoSpace()
Purpose: Returns true if running under ExoSpace.
Syntax: int ExoIsExoSpace(void);
Returns: This function returns nonzero if an ExoSpace program is running.
See also: ExoIsDPMI(), ExoIsPM()
Notes: Use this function to determine whether your program is running
under ExoSpace. If the program has been linked with the real
mode "stub" library, this function will return zero.
Example: This program fragment prints a message telling whether or not the
program executing is an ExoSpace application.
if (ExoIsExoSpace())
printf("ExoSpace program is running");
else
printf("ExoSpace program is not running");
ExoIsPM()
Purpose: Returns true if the CPU is running in protected mode.
Syntax: int ExoIsPM(void);
Returns: Nonzero if the CPU is executing in protected mode, zero
otherwise.
See Also: ExoIsDPMI(), ExoIsExoSpace()
Notes: Use this function to determine whether the processor is
running in protected mode.
Example: This program fragment prints a message telling whether or
not the CPU is in protected mode.
if (ExoIsPM())
printf("cpu is in protected mode");
else
printf("cpu not in protected mode");
int ExoIsVMM(void);
Purpose: Returns true if the ExoSpace program is using the ExoSpace VMM
system.
Syntax: int ExoIsVMM(void);
Returns: Nonzero if the program is using the ExoSpace VMM system, zero
otherwise.
See Also: ExoIsDPMI(), ExoIsExoSpace()
Notes: Use this function to determine whether the ExoSpace VMM system is
active. It will not be active if the option "EXOSPACE PACKAGE
NOVM" was specified during the link or if you are running under a
DPMI server such as a Windows or OS/2 DOS box.
Example: This program fragment prints a message telling whether or not the
program is using the ExoSpace VMM system.
if (ExoIsVMM())
printf("program is using the ExoSpace VMM system");
else
printf("program is not using the ExoSpace VMM system");
ExoProtectedPtr()
Purpose: Creates a protected mode pointer from a real mode pointer.
Syntax: void *ExoProtectedPtr(void *rm_ptr, unsigned size);
Returns: The protected mode pointer, or NULL if the pointer can't be
allocated. A new descriptor is allocated.
See also: ExoRealPtr(), ExoFreeSelector()
Notes: The protected mode data pointer created by this function points
to a segment of size bytes. Since a value of 64KB is too large
for an unsigned variable, use 0 to specify 64KB for size. The
offset of pm_ptr is always 0. The base physical address of
pm_ptr's segment is the absolute address of rm_ptr, which is
segment(rm_ptr) * 16 + offset(rm_ptr). Because ExoProtectedPtr()
is run with interrupts enabled, realtime applications should not
call it from an external interrupt handler.
The offset of the pointer returned by ExoProtectedPtr() will
ALWAYS be 0.
ExoFreeSelector() MUST be called to free the selector allocated
with ExoProtectedPtr(). Selectors are a limited resource and
repeated calls to ExoProtectedPtr() as well as ExoSegCSAlias()
and ExoSegDSAlias() could use up all the available selectors
causing the program to crash. Once you are done with a pointer
created with ExoProtectedPtr(), free the selector unless you'll
be using the pointer thoughout the program.
Example: See example for ExoFreeSelector().
ExoRealPtr()
Purpose: Creates a real mode pointer from a protected mode pointer.
Syntax: void *ExoRealPtr(void *pm_ptr);
Returns: The real mode pointer, or NULL if pm_ptr is in extended memory
(not accessible in real mode).
See also: ExoProtectedPtr()
Notes: The pm_ptr argument is the protected mode pointer to be
converted. Note that if the real mode pointer is 0:0, the
returned value is the same as an error value.
Example: See example for _xalloclow().
ExoReside()
Purpose: Flags a VMM segment to remain resident in memory.
Syntax: int ExoReside(void *pmptr);
Returns: -1 on error, 0 or 1 otherwise.
See also: ExoSegDSAlias()
Notes: When the VMM system is active a code or data segment may be
swapped out at any time. When data segments are swapped out they
are always written to the swap file, but code segments are never
written to the swap file when they are swapped out unless a data
segment alias created by ExoSegDSAlias() exists at the time the
code segment is swapped out. If you write to a data segment
alias of a code segment when ExoSpace's VMM is active, you will
need to mark the segment as resident before freeing the data
segment alias with ExoFreeSelector().
Example: See example for ExoSegDSAlias().
ExoRMInterrupt()
Purpose: Sets the registers, then signals a real mode interrupt.
Syntax: int ExoRMInterrupt(int intno, EXOREGS *inregs, EXOREGS *outregs);
Returns: The flags register.
See also: None
Notes: Use ExoRMInterrupt() to signal a real mode interrupt that
requires you to set registers. This function allows you to set
real mode segment register values from protected mode. When you
use regular passdown interrupts, you can't set real mode register
values.
A call to ExoRMInterrupt() sets the registers to the values you
provide in the structure to which inregs points; then it invokes
interrupt intno. After the interrupt is processed,
ExoRMInterrupt() stores the register values in the structure
pointed to by outregs. The structures pointed to by inregs and
outregs are both type EXOREGS. Note that you cannot set the SS
and SP registers from a EXOREGS structure.
Before ExoSpace signals the specified interrupt, it sets the CPU
flags to the value they had when you called ExoRMInterrupt() in
protected mode. Notice that the value of the flags register
after the real mode interrupt call is made is returned by this
function.
When you use ExoRMInterrupt(), there must be a return from the
real mode interrupt to match every call (unless your program is
terminating). Also, if reentrance on ExoRMInterrupt() is
possible, the interrupt should use less than 256 bytes of stack.
You can get around this limit by switching to a different stack
in the real mode handler/function.
Example: This example signals DOS function 2Ch to get the time of day. As
required for that call, the high order byte of the AX register is
set to 2Ch. Note that the structure EXOREGS does not contain a
field AH, so you must set the high order byte using the shift
operator (<<). After the call to ExoRMInterrupt(), this example
displays the contents of the outreg registers. The display shows
the time in hours, minutes and seconds. Since the hours and
seconds are stored in the high order byte, the shift operator is
used again.
int main()
{
EXOREGS inreg;
EXOREGS outreg;
inreg.ax = 0x2C << 8;
/* DOS function "get time" - signal int 21h */
ExoRMInterrupt(0x21, &inreg, &outreg);
printf("The time is %d:%02d:%02d\n",
outreg.cx >> 8, outreg.cx&0xFF, outreg.dx >> 8);
}
ExoSegCSAlias()
Purpose: Creates a code descriptor for the given data segment.
Syntax: void (*ExoSegCSAlias(void *pm_ptr))();
Returns: A function pointer to the same linear address as pm_ptr, or NULL
if an error occurred.
See also: ExoSegDSAlias()
Notes: The new descriptor created by ExoSegCSAlias points to the segment
base of pm_ptr. This function enables you to execute code in a
data segment. To store data in a code segment, use
ExoSegDSAlias(). Because ExoSegCSAlias() is run with interrupts
enabled, realtime applications should not call it from an
external interrupt handler.
The offset of the alias will ALWAYS equal the offset of the
original pointer.
ExoFreeSelector() MUST be called to free the selector allocated
with ExoSegCSAlias(). Selectors are a limited resource and
repeated calls to ExoSegCSAlias() as well as ExoSegDSAlias()
and ExoProtectedPtr() could use up all the available selectors
causing the program to crash. Once you are done with a pointer
created with ExoSegCSAlias(), free the selector unless you'll
be using the pointer thoughout the program.
Example: This code fragment calls executable machine code written by the
program to a data buffer.
char writablecode[100];
void (*funcptr(void));
/* during execution the program would write executable machine
code to the 100 byte writablecode buffer */
...
/* create a selector that is a code segment alias to the data
segment containing the variable writablecode */
funcptr = ExoSegCSAlias(writablecode);
/* set the offset of the function pointer to the offset of the
writablecode buffer */
FP_OFF(funcptr) = FP_OFF(writablecode);
/* call the code written to the writablecode buffer */
*funcptr();
/* cancel the code segment selector */
ExoFreeSelector(FP_SEG(funcptr));
ExoSegDSAlias()
Purpose: Creates a data descriptor for the given code segment.
Syntax: void *ExoSegDSAlias(void *pm_ptr());
Returns: A data pointer to the same linear address as pm_ptr, or NULL if
an error occurred.
See also: ExoSegCSAlias()
Notes: This function creates a new data descriptor with the same base as
the segment specified by pm_ptr. After the call to
ExoSegDSAlias(), the descriptor indicated in pm_ptr still exists
and retains its original segment type.
The offset of the alias will ALWAYS equal the offset of the
original pointer.
ExoFreeSelector() MUST be called to free the selector allocated
with ExoSegDSAlias(). Selectors are a limited resource and
repeated calls to ExoSegDSAlias() as well as ExoSegCSAlias()
and ExoProtectedPtr() could use up all the available selectors
causing the program to crash. Once you are done with a pointer
created with ExoSegDSAlias(), free the selector unless you'll
be using the pointer thoughout the program.
Example: The example below consists of two modules: one in assembly
language and one in C. The int_instruction label in the assembly
code marks the location of an instruction signaling Interrupt 0h.
The C code overwrites this instruction so that a different
interrupt is signaled.
The C code calls ExoSegDSAlias() to get an alias data pointer to
int_instruction. The example then increments the pointer by one
byte to point to the location of "0h" in the instruction. The
example overwrites this location with "0x88" and frees the data
descriptor with ExoFreeSelector() since it is no longer needed.
public _do_int, int_instruction
_do_int:
...
int_instruction:
int 0h
...
char *data;
extern int_instruction();
/* create data segment alias and write interrupt number 0x88 */
data = ExoSegDSAlias(int_instruction);
*(data + 1) = 0x88;
/* mark code segment as resident so the VMM system won't swap it
out and lose the changes made to it */
ExoReside(int_instruction);
/* free the selector created by ExoSegDSAlias() since we don't
need it anymore */
ExoFreeSelector(FP_SEG(data));
_xalloclow()
Purpose: Allocates low DOS memory for use with real mode interrupts that
must be passed buffers within the first 1 MB.
Syntax: void *_xalloclow(unsigned int sizebytes);
Returns: A protected mode pointer to the allocated memory.
See also: ExoRealPtr(), ExoRMInterrupt(), _xfreelow()
Notes: This function returns a protected mode pointer to the allocated
memory. Use this pointer to write to or read from the allocated
buffer. When calling a real mode interrupt you must get a real
mode pointer to the allocated buffer with the ExoRealPtr()
function and pass that address in the EXOREGS structure of a
ExoRMInterrupt() call.
You can allocate a 64K buffer by passing a size of 0.
You must use _xfreelow() to free memory allocated with this
function. Do not use xfree()!
It is suggested that you free the allocated low DOS memory as
soon as possible to keep down the amount of low DOS memory that
may be allocated at the same time not only by your functions
but also by other C/ASM code or by other third party libraries.
ExoSpace has added the LOWMEM parameter to the CLIPPER
environment variable to allow a developer to set aside low DOS
memory for allocation with _xalloclow(). The LOWMEM parameter
does not have to be specified in order for _xalloclow() to work;
if there is no available low DOS memory when _xalloclow() is
called, ExoSpace will free up low DOS memory it uses in order to
meet the request. At this time because of the current design of
ExoSpace's memory allocation scheme we recommend that you ignore
the LOWMEM parameter. Future revisions or individual end-user
needs may make the LOWMEM parameter more necessary.
Example: This example maps a fake root directory of a specified drive
under Novell NetWare. The function below accepts a drive number
(0=default, 1=A:, ...) and path for fake root; it returns
zero on success and an error code if the function failed.
int maproot(int drive, char *path)
{
int returnvalue = 0xFFFF;
int flags;
char *buffer;
char *realptr;
EXOREGS inoutregs;
/* allocate low memory buffer to store path */
if ((buffer = _xalloclow(strlen(path) + 1)) != NULL)
{
/* store path in low memory buffer */
strcpy(buffer, path);
/* get real mode pointer to low memory buffer */
if ((realptr = ExoRealPtr(buffer)) != NULL)
{
/* fill in EXOREGS structure */
inoutregs.ax = 0xE905;
inoutregs.bx = drive;
inoutregs.ds = FP_SEG(realptr);
inoutregs.dx = FP_OFF(realptr);
/* call the real mode interrupt */
flags = ExoRMInterrupt(0x21, &inoutregs, &inoutregs);
/* see if carry flag is set to indicate error */
if (flags & 0x0001)
/* return error code in al */
returnvalue = (inoutregs.ax & 0x00FF);
else
/* return no error code */
returnvalue = 0;
}
/* free low memory buffer */
_xfreelow(buffer);
}
}
_xfreelow()
Purpose: Frees memory allocated by _xalloclow().
Syntax: void _xfreelow(void *lowmemory);
Returns: Nothing.
See also: _xalloclow()
Notes: You must use this function to free memory allocated by
_xalloclow(). Do not use xfree() to free memory allocated by
_xalloclow()!
Example: See _xalloclow() example.
Problem Solving
---------------
Question: How do I determine if I am running under ExoSpace?
Answer: Simply use the ExoIsExoSpace() function.
Question: How do I determine if I am running in protected mode?
Answer: Simply use the ExoIsPM() function. This function has little use
because ExoSpace does not document a way to switch to real mode.
Question: How do I determine if Rational's VM system is active?
Answer: Simply use the ExoIsVMM() function. If the application was
created with the "EXOSPACE PACKAGE NOVM" option, this function will return
zero (false).
Question: How do I determine if I am running under DPMI?
Answer: Simply use the ExoIsDPMI() function. Running under DPMI is more
restrictive than running under any other memory manager and although you
should construct your software to run under DPMI you could choose to
restrict your software to run under non-DPMI memory managers.
Question: I really need to write to data in the code segment. How can I do
so?
Answer: You can write data to the code segment is you create a "DS alias".
You can NEVER write to the code segment using the CS selector register,
but you can create an alias using the ExoDSAlias() function. This
function returns a selector that can be loaded into the DS or ES
registers. This selector points to the same memory used as a code segment.
Question: I really need to execute code I have created in a data segment.
How can I do so?
Answer: You can use the ExoCSAlias() to create a code selector that
corresponds to a data segment. You can then CALL or JMP to the code
selector.
Question: I want to address an absolute memory location such as the color
video memory at 0xB8000. How do I do so?
Answer: When not running under DPMI, many of the most used real mode
segments are matched by protected mode selectors that are mapped to the
same physical memory locations. When running under DPMI (such as in a
Windows DOS box) the only selector that is predefined to match its real
mode segment is selector 0x40 which points to the BIOS data area. Under
DPMI, no other selector is predefined so trying to use selector 0xB800 to
access color video memory will generate a GP fault.
Note: Even when not running under DPMI, code which uses selector 0 to
access the BIOS data area will generate a GP fault. This is because
selector 0 is a special NULL selector that cannot be read from or written
to.
Under DPMI it is necessary to use the ExoProtectedPtr() function to create
a selector which points to a physical memory location such as segment
0xB800. It is recommended that you use ExoProtectedPtr() for maximum
portability since it also works when not running under DPMI. Simply pass
ExoProtectedPtr() a real mode pointer to a physical memory address in the
lower 1 MB and a size. It will return a pointer to that memory address.
The size parameter determines the size of the protected mode segment that
the selector points to. Attempting to access an offset greater than the
size of the segment will generate a GP fault; again, a very good
protection against writing to memory outside of a designated range.
Question: How do I call an interrupt in real mode?
Answer: If the interrupt you are calling is a documented DOS interrupt, it
will be handled transparently by ExoSpace. In addition, some interrupts
which are not normally handled transparently by ExoSpace will be handled
if the application is linked with one of the ExoSpace packages. For
example, ExoSpace does not handle the mouse interrupt transparently unless
you link in the mouse package with the command "EXOSPACE PACKAGE MOUSE".
Interrupts which do not pass pointers to data, but only pass values in
registers can be called from protected mode with no modifications to your
code. This works because ExoSpace will trap the interrupt, check to see
if it is a special case which ExoSpace handles, and if not, it will
reissue the interrupt in real mode. The only problems occur when the real
mode interrupt expects a pointer to a data buffer to be in a register pair
such as ES:DI. When the interrupt is reissued in real mode the pointer is
no longer valid because it was a protected mode pointer and it will
instead point to garbage in the first 1 MB.
To issue an interrupt you can use C's int86..() functions or ExoSpace's
ExoRMInterrupt() function. ExoSpace's ExoRMInterrupt() function should be
slightly faster in most circumstances.
Question: How do I pass a pointer to data to a real mode interrupt?
Answer: You can use the ExoRealPtr() function to convert a protected mode
pointer to a real mode pointer. The return value from ExoRealPtr() should
be loaded into the REGS structure that ExoRMInterrupt() or int86..() uses.
The protected mode pointer must point to a memory location within the
first 1 MB.
Question: How do I allocate a block of memory so that I know it is in the
first 1 MB and therefore addressable in real mode?
Answer: Use the _xalloclow() function to allocate memory in the lower 1
MB. This is only needed for buffers that are passed to real mode
interrupt handlers which can only address the first 1 MB. Use _xfreelow()
to free the allocated memory.
It is recommended that your code allocate and then free low DOS memory
after every use to keep the amount of allocated low DOS memory in use at
any one time down to a minimum. Currently, _xalloclow() will steal memory
from Clipper if it must to meet the request. Allocating a great deal of
low DOS memory could adversely affect performance.
Question: How can I have the user reserve an amount of low DOS memory for
allocation by my program/library?
Answer: ExoSpace has added the LOWMEM parameter to the CLIPPER environment
variable. This parameter specifies the amount of low DOS memory to
reserve for allocation with _xalloclow(). If your program/library
allocates up to 25K at one time with _xalloclow(), the user could specify
a LOWMEM parameter of 25K. Actually, it is recommended at this time that
you ignore the LOWMEM parameter due to the design of ExoSpace's low DOS
memory allocation scheme, but this may change in the future or a particular
end-users needs may require the LOWMEM parameter.
Question: How do I install an interrupt handler?
Answer: In most cases you can install a software interrupt handler using
the DOS function 0x25. This requires no changes to your source code.
Conflicts can occur when installing hardware interrupt handlers. ExoSpace
handles hardware interrupts in various ways and it is beyond the scope of
this document to discuss hardware interrupts. If you are installing a
hardware interrupt, see if it works. If it doesn't, contact us. If it
does work be aware that hardware interrupts that occur frequently such as
serial port interrupts may degrade system performance and even come too
fast to be handled properly without special programming. If you are
dealing with serial hardware interrupts or other fast hardware interrupts
you are probably a candidate for the full version of DOS/16M. It contains
more documentation and example code for handling fast hardware interrupts.
Question: How can I allocate and use more than 64K at one time?
Answer: Currently, there is no documented ExoSpace API function to do
this. The DOS memory allocation function can allocate more than 64K at
one time, but only the first 64K can be addressed by the returned
selector. A special kind of selector arithmetic must be done to address
data beyond the first 64K. A future API revision may include functions to
use more than 64K of data, but for now it is best to allocate large
amounts of memory in 64K chunks.
Sample Code
-----------
We have provided some sample code with this draft API. Additional code
samples will be forthcoming in the next two weeks.