Category : Assembly Language Source Code
Archive   : XLIB20.ZIP
Filename : XLIB.DOC

 
Output of file : XLIB.DOC contained in archive : XLIB20.ZIP






























XLIB PROGRAMMER'S MANUAL
VERSION 2.0

(DOS Extender Library)

TechniLib Company























Copyright 1993, by TechniLib (TM) Company
All Rights Reserved






TERMS OF USE AND DISTRIBUTION


XLIB is a shareware product; therefore, unregistered copies of XLIB are
made available free of charge so that potential purchasers will have the
opportunity to examine and test the software before committing payment.
Distribution of unregistered copies of XLIB to other potential users is also
permitted and appreciated. However, usage and distribution of XLIB must
conform to the following conditions. In the following statement, the term
"commercial distribution," includes shareware distribution.

1) XLIB and accompanying software must be distributed together in copies of
the original archive provided by TechniLib. Neither the archive nor
individual files therein may be modified.

2) The XLIB archive may be distributed in combination with other shareware
products; however, the XLIB archive may not be distributed with other
commercially distributed software without written consent of TechniLib.

3) Copies of XLIB which have been used to develop software for commercial
distribution must be registered before such software is marketed. Copies of
XLIB which have been used to develop noncommercial software must be registered
if such software is to be regularly used either by the developer or others.

4) Commercially distributed software must embed XLIB procedures in the
software code. Files contained in the XLIB archive may not be placed in the
distribution media.

5) XLIB is designed to offer a set of services to other executable code. XLIB
may not be used to develop software for commercial distribution which will
essentially offer any of these same services to other executable code.
Exceptions to this condition require written consent of TechniLib.

6) Rights afforded by registering a single copy of XLIB pertain only to a
single computer.

7) XLIB may be registered for a fee of $35.00 per copy. Accompany payment
with the registration form included in the XLIB archive. Registrants will be
entitled to the most recent version of the XLIB archive.


DISCLAIMER OF WARRANTY


XLIB AND ALL ACCOMPANYING SOFTWARE AND LITERATURE ARE DISTRIBUTED WITH
THE EXCLUSION OF ANY AND ALL IMPLIED WARRANTIES, AND WITH THE EXCLUSION OF
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. TechniLib
SHALL HAVE NO LIABILITY FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
RESULTING FROM THE USE OF XLIB OR ACCOMPANYING MATERIALS. The user assumes
the entire risk of using this software.


Copyright 1993, by TechniLib (TM) Company
All Rights Reserved






TABLE OF CONTENTS


CHAPTERS
Page
1. Introduction 1
2. XLIB Conventions and Structure 4
3. XLIB Initialization and Termination 6
4. Mode Switching 9
5. Inline Mode Switching 13
6. Interrupt Management 16
7. Memory Management 22
8. File Management 25
9. Descriptor Management 29
10. Using XLIB in High-Level Language Libraries 30


TABLES
Page
1. XLIB Segments and Selectors by Public Symbol 5
2. CALLPM/ENTERPM Register Storage Locations by Public Symbol 11
3. CALLRM Register Storage Locations by Public Symbol 12
4. XLIB File Control Block Structure 25


EXAMPLES
Page
1. Simple Mode Switching Under XLIB 3
2. Using INLINEPM/INLINERM in C 13
3. Calling Protected-Mode Libraries From BASIC 30


APPENDICES
Page
A. Description of XLIB Public Data 34
B. XLIB Error Codes 37
C. DPMI 1.0 Error Codes 39
D. XMS Error Codes 40
E. Calling Protected-Mode Libraries From C 41
F. Reporting Problems With XLIB 43
G. The SWITCHPM and SWITCHRM Procedures 44






1. Introduction


XLIB is an assembly language library which may be used with assembly
language programs to greatly simplify protected-mode programming under the
Microsoft DOS operating system. Implementation of protected-mode procedures
will often require no more than placing code in the proper segment and making
two calls to XLIB procedures. XLIB also simplifies protected-mode programming
in C using inline assembly.
XLIB is designed for the Intel 386, 486, and Pentium processors. XLIB
fully utilizes the 32-bit processing powers of these chips and makes these
powers available to the user. The compactness of XLIB follows largely from
the fact that much of it is written in 32-bit code.
XLIB is used to produce extended DOS applications. DOS is unfortunately
limited by the fact that it is a real-mode operating system intended to manage
only real-mode programs. Real-mode programs cannot use memory addresses
requiring more than 20 bits, or use memory offsets requiring more than 16
bits. Such programs are further limited by the fact that 32-bit instructions
execute awkwardly in real-mode. When the processor is in real-mode, it will
expect all 32-bit instructions to be preceded by at least one prefix. Each of
these prefixes consumes one byte of memory and requires at least one clock to
execute. Such limitations do not exist in 32-bit protected mode. Extended
DOS applications overcome the limitations of DOS with their ability to execute
in both real and protected modes. DOS services can be utilized from real mode
while the 32-bit processing power of the CPU can be utilized from protected
mode.
There are presently several 32-bit operating systems available on the
market. Such systems include IBM OS/2, Microsoft Windows NT, and UNIX. These
systems can manage programs which operate exclusively in protected mode. Such
programs are generally easier to develop than extended DOS applications.
However, extended DOS applications have an advantage in that they can execute
under all of these operating systems because all of these systems can emulate
DOS.
Of course DOS will eventually become obsolete. Future programs will
likely operate exclusively in protected mode using the flat memory model. The
memory models supported by XLIB approximate the flat model; therefore, code
written for XLIB will require little modification when being transported to
flat-model operating environments. Indeed, many procedures will require no
modification whatsoever. Moreover, XLIB includes flat-model descriptors which
may be used to execute genuine flat-model code. XLIB does not load and
relocate such code; however, it does provide all the necessary tools to
develop such procedures.
XLIB procedures handle important tasks such as mode switching between
real and protected modes, memory management under protected mode, protected-
mode interrupt management, and protected-mode file management. XLIB includes
routines to perform these tasks in the absence of a protected-mode interface,
or in the presence of the Virtual Control Program Interface (VCPI), or the DOS
Protected Mode Interface (DPMI, version .9 or higher). XLIB can also manage
extended memory through the Extended Memory Specification (XMS).
Upon initialization, XLIB will examine the operating environment for the
presence of DPMI, VCPI, and XMS, and then configure itself accordingly. The
client program may therefore perform calls to XLIB procedures with few
concerns as to the environment in which it is executing.




1






XLIB relieves the programmer of descriptor table management by supplying
a set of predefined segments along with their associated descriptors and
selectors. Many protected-mode procedures will require no modification for
XLIB other than being placed in the proper segment. XLIB provides a single
32-bit segment for protected-mode routines. This segment may be larger than
64K, but must reside in conventional memory so that DOS can load it. However,
code within this segment may access data throughout the address space. XLIB
does allow user-defined descriptors; however, these should be needful only in
very rare cases.
The following program illustrates the simplicity with which protected-
mode execution may be initiated and terminated with XLIB. The program was
written with the Microsoft Assembler (MASM). It first initializes XLIB by
calling a procedure called INITXLIB. After confirming that initialization is
successful, the program then transfers control to a 32-bit protected-mode
procedure which prints a message to the screen. Control is transferred by
placing the protected-mode target address on the stack and then calling an
XLIB procedure named CALLPM (call protected mode). CALLPM will expect the
target procedure to be contained in a segment called TSEG. The protected-mode
procedure in TSEG returns control to real or virtual 8086 (V86) mode simply by
executing the RET instruction.





































2






Example 1: Simple Mode Switching Under XLIB
-----------------------------------------------------------------------------
.MODEL LARGE,PASCAL
.386P

INCLUDE XLIB.INC ;Include XLIB public symbols
INCLUDELIB XLIB.LIB ;Link with XLIB.LIB

.STACK 1024
.CODE
.STARTUP
CALL INITXLIB ;Initialize XLIB
OR EAX,EAX ;EAX = 0 if successful
JZ INITDONE
.EXIT 0 ;Initialization failed
INITDONE: PUSHD OFFSET DEMOPROC
CALL CALLPM ;Execute DEMOPROC in protected
.EXIT 0

;Protected-mode routines must be placed in following segment:
TSEG SEGMENT PARA PUBLIC USE32 'CODE'
ASSUME CS:TSEG, SS:TSEG, DS:TSEG, ES:TSEG, FS:DSEG, GS:DGROUP

;Protected-mode routine to print message to the screen using DOS function.
DEMOPROC PROC NEAR
MOV EBX,OFFSET PMMSG
MOV AH,02H
MSGLOOP: MOV DL,CS:[EBX] ;32-bit offset!!!!!
OR DL,DL
JZ EXIT
INT 21H ;Print character with DOS
INC EBX
JMP MSGLOOP
EXIT: RET ;Go back to real or V86 mode
PMMSG DB "In 32-bit protected mode!!! "
DB "Returning to real mode.",10,13,0
DEMOPROC ENDP

TSEG ENDS
END
-----------------------------------------------------------------------------


The framework presented in the above program is extremely simple;
nonetheless, it will meet the demands of many protected-mode programs. Most
protected-mode programs will require no further modifications other than a few
calls to XLIB extended memory management procedures.
XLIB was developed and tested under Microsoft DOS version 6.0 using
Microsoft Assembler (MASM) version 6.1a, Microsoft LINK version 5.31.009, and
Microsoft LIB version 3.20.01. MASM parameters were set to c, W2, and WX.
LINK parameters were set to BATCH, CPARM:1, FAR, NOPACKF, and PACKC. XLIB has
also been tested under Microsoft Windows 3.1, Qualitas 386MAX version 6.02,
Quarterdeck QEMM version 6.02, Quarterdeck QDPMI version 1.01, Quarterdeck
DESQview version 2.42, and IBM OS/2 version 2.1.



3






2. XLIB Conventions and Structure


XLIB public procedures and public data are explained in the following
chapters. A detailed explanation of most XLIB public data is also included in
Appendix A. This chapter sets forth rules which will be generally applicable
to all XLIB data and procedures.
Though it is sometimes necessary for XLIB to distinguish between real
mode and virtual 8086 mode; this document uses the term "real mode" to include
virtual 8086 mode.
All public real-mode procedures and 16-bit protected-mode procedures in
XLIB are located in a segment called CSEG. The user may also place code in
CSEG but is rarely required to do so. All public XLIB procedures in CSEG have
far returns.
All public 32-bit protected-mode procedures in XLIB are located in a 32-
bit segment called TSEG and have near returns. XLIB will also expect the user
to place all 32-bit procedures in TSEG and will expect these procedures to
have near returns. This policy is adopted to achieve approximation to the
flat model.
Nearly all XLIB protected-mode procedures are 32-bit routines. The only
exceptions to this rule are the inline mode-switch procedures discussed in
Chapter 5. This policy is implemented to approximate the flat model. There
are very few circumstances in which 16-bit protected mode is preferable to 32-
bit protected mode. One can generally increase program speed and reduce
program size by writing the code for 32-bit segments.
TSEG may be larger than 64K provided that certain rules are observed.
XLIB adheres to all of the necessary rules. First, only 32-bit protected-mode
code should be placed in TSEG. The processor will not generally be able to
execute real-mode code in this segment because the offsets will be 32-bit
values. Second, real-mode code should never write to or read from TSEG. Such
instructions will also require 32-bit offsets. Modifications to TSEG should
be done from a TSEG protected-mode procedure. Finally, segment constants
should never be encoded in TSEG. For example, the symbols CSEG, TSEG, DSEG,
and DGROUP should not be found in TSEG. DOS will not be able to perform
relocation edits on these constants if they are in a segment larger than 64K.
To read these values from TSEG, initialize memory locations in a 16-bit
segment to the values and then read the memory locations. Memory locations in
DSEG have already been initialized for XLIB segments (see Appendix A).
Microsoft LINK will issue a warning for code segments exceeding 64K; however,
this warning may be safely ignored provided that the above rules are observed.
All XLIB procedures may be called with interrupts enabled and will return
with interrupts enabled provided that they were enabled upon call.
All XLIB public data is contained in a 16-bit segment called DSEG. The
user may also place data in DSEG but is rarely required to do so.
XLIB uses the PASCAL calling and naming convention. The PASCAL
convention is equivalent to the BASIC/FORTRAN convention. C programmers must
adapt XLIB procedures and symbols with declarations which specify the PASCAL
convention. The header file XLIB.H contains such declarations.
XLIB routines which can encounter error conditions will always return
XLIB error codes in AX. A list of such error codes is presented in Appendix
B. In most cases, DX or the high word of EAX will be returned with specific
information about the error, such as DPMI, XMS, or DOS error codes. DPMI
error codes are presented in Appendix C. XMS error codes are in Appendix D.




4






Selectors for all XLIB segments are placed in public WORD locations in
segment DSEG. The following table gives the name of each predefined selector
along with its associated segment name and description:


Table 1: XLIB Segments and Selectors by Public Symbol
-----------------------------------------------------------------------------
Selector Name Segment Name Description
------------- ------------ -----------
CSEGSEL CSEG XLIB 16-bit code segment
CSEGDSEL CSEG Data selector to CSEG
TSEGSEL TSEG 32-bit code segment
TSEGDSEL TSEG Data selector to TSEG
DSEGSEL DSEG XLIB data segment
FLATSEL . Flat-model code
FLATDSEL . Flat-model data
DGROUPSEL DGROUP DGROUP data group
SCRNSEL . Screen data
MAINCSSEL . CS selector for main caller
MAINSSSEL . SS selector for main caller
MAINDSSEL . DS selector for main caller
MAINESSEL . ES selector for main caller
ILCSSEL . Inline CS selector
ILSSSEL . Inline SS selector
ILDSSEL . Inline DS selector
-----------------------------------------------------------------------------


The flat-model and TSEG descriptors have limit FFFFFFFFH. All other
descriptors have limit FFFFH. The screen descriptor has base set to B8000H
for color monitors and B0000H for monochrome monitors. MAINCSSEL, MAINSSEL,
MAINDSSEL, and MAINESSEL are selectors to descriptors which have base
addresses matching the contents of CS, SS, DS, and ES as of the call to
INITXLIB. ILCSSEL, ILSSSEL, and ILDSSEL are selectors used by the inline
mode-switch procedures (see Chapter 5). The base addresses of the
corresponding descriptors are adjusted dynamically.
All data segments are writable. The data descriptors have their big bits
set; consequently, implicit stack instructions will use ESP rather then SP.
All code segments are readable and nonconforming. Descriptor privilege levels
and requested privilege levels are set to zero unless DPMI is installed.
Privilege levels under DPMI will generally be set to three.
The values contained in the above selectors will be different under DPMI
than other environments. Moreover, DPMI selector values can differ in
different environments and under different hosts. The user should therefore
always read selector values from these locations.
Since selectors are stored in DSEG, the user must never lose track of the
DSEG selector. This could prove a problem in interrupt handlers where no
assumptions can be made as to register contents. Consequently, XLIB places a
copy of the DSEG selector in TSEG where it can always be found. It is stored
under WORD symbol CSDSEGSEL and may be read with CS override. For example
from TSEG code you can always load DS with DSEGSEL using MOV DS,CS:CSDSEGSEL.
XLIB never uses selectors past DGROUPSEL in Table 1. The user may
therefore redefine the associated descriptors if desired. Selector values
however should not be changed.



5






3. XLIB Initialization and Termination


XLIB procedures apart from those presented in this chapter should be
called only after XLIB has been initialized by calling INITXLIB. This routine
will examine the operating environment for the presence of DPMI, VCPI, and
XMS. It will then perform extensive code modifications upon XLIB to
accommodate the resident software. INITXLIB is to be called only once within
a program. Subsequent calls have no effect.
If XLIB finds that neither DPMI nor VCPI are present, then XLIB will
completely handle all mode switching and interrupt management. If XLIB finds
that XMS is absent also, then XLIB will handle all extended memory management.
If XLIB finds that both DPMI and VCPI are present, then it will configure
itself for DPMI by default. However, the default may be changed by setting
bit 0 of IFLAGS (initialization flags) before calling INITXLIB. If this bit
is set, then VCPI is given priority over DPMI. IFLAGS is a public WORD
location in DSEG.
INITXLIB will probably attempt to allocate some conventional memory
through DOS. Since high-level language modules and assembly language modules
often claim all available DOS memory, INITXLIB may fail for lack of available
DOS memory. This problem can be averted with assembly language modules by
linking with the CPARM:1 parameter. This forces the module to claim no more
DOS memory than is necessary. High-level language modules should call
XLIBMEMREQ (XLIB memory requirements) to obtain conventional memory
requirements and then release at least this much memory. This process is
illustrated for Microsoft BASIC in Chapter 10. C does not allocate all DOS
memory and therefore does not require such special action.
If both VCPI and DPMI are present, then conventional memory requirements
will depend upon which of these interfaces is to be chosen by INITXLIB. In
such cases, XLIBMEMREQ will return DPMI memory requirements if bit 0 of IFLAGS
is clear, and will return VCPI requirements otherwise. Therefore, this bit
should be set to the appropriate value by the user before calling XLIBMEMREQ.
DPMI conventional memory requirements may be obtained by calling DPMIMEMREQ.
These will vary from host to host. VCPI conventional memory requirements may
be obtained by calling VCPIMEMREQ. VCPI requirements are the same for all
servers.
One of the principle function of XLIB is of course to make protected-mode
interfaces and memory management interfaces transparent to the client program.
However, there are special cases where a program should be informed as to
which interfaces are implemented. In such cases, the procedure XLIBCONFIG
(XLIB configuration) may be called to determine the how XLIB has been
configured by INITXLIB.
XLIB is terminated with INT 21H function 4C (DOS termination) issued
either from real or protected mode.













6






Detailed Specifications


INITXLIB (Initialize XLIB)
Purpose: Check for presence of XMS, DPMI, and VCPI, then configure XLIB to
operate with the installed interfaces.
CPU Mode: Real
Registers at Call: None
Return Registers:
AX = 0 if successful, in which event DX and EAX are zero as well. The
configured interfaces may be identified by calling XLIBCONFIG (see below).
AX <> 0 if unsuccessful. An XLIB error code is returned in AX. A specific
error code is returned in DX and in the high word of EAX (EAH). If AX = 11H
or 12H, then DX and EAH will equal a DOS error code. If AX = DPMI error, then
DX and EAH will equal a DPMI 1.0 error code (if provided by host). If AX =
VCPI error, then DX and EAH are returned as zero.
Details:
If both DPMI and VCPI are present, then XLIB will be configured for DPMI if
the zero bit of IFLAGS is clear. If this bit is set, then XLIB will be
configured for VCPI. The bit is clear by default.
This routine will likely attempt to allocate DOS memory. The amount of DOS
memory XLIB will attempt to allocate can be obtained by calling XLIBMEMREQ
(see below).
Descriptors are created for the segments loaded in CS, SS, DS, and ES as of
call to this routine. The selectors for these descriptors are MAINCSSEL,
MAINSSSEL, MAINDSSEL, and MAINESSEL. These descriptors are never used by
other XLIB procedures. They are provided to augment development of protected-
mode libraries. Protected-mode library routines may access the stack and data
of the main program through these descriptors.
INITXLIB should be called only once within a program. Subsequent calls are
returned with no action. XLIB is terminated by INT 21H function 4C (DOS
termination) issued from either real or protected mode.

XLIBMEMREQ (XLIB Memory Requirements)
Purpose: Find XLIB conventional memory requirements.
CPU Mode: Real
Registers at Call: None
Return Registers:
Sign bit of DX clear if successful. Memory requirements in bytes are
returned in DX:AX.
Sign bit of DX set if unsuccessful. An error code is returned in AX
(always a DOS error code).
Details:
DX:AX is adjusted upward to an integer multiple of 16.
If both DPMI and VCPI are present, then XLIBMEMREQ will assume that DPMI
will be used if bit 0 of IFLAGS is clear (the default); otherwise, VCPI is
assumed. No additional conventional memory is needed if both DPMI and VCPI
are absent.
This routine will return DX:AX = 0 if XLIB contains free internal memory in
sufficient quantity to meet conventional memory demands.
This routine obtains DPMI requirements by calling DPMIMEMREQ (see below)
and VCPI requirements by calling VCPIMEMREQ.





7






DPMIMEMREQ (DPMI Memory Requirements)
Purpose: Find DPMI conventional memory requirements.
CPU Mode: Real
Registers at Call: None
Return Registers: DX:AX = conventional memory requirements.
Details:
DX:AX is adjusted upward to an integer multiple of 16.
This routine does not assume the presence of DPMI. It will return DX:AX =
0 if DPMI is absent.
This routine will return DX:AX = 0 if XLIB contains free internal memory in
sufficient quantity to meet the conventional memory demands of DPMI.

VCPIMEMREQ (VCPI Memory Requirements)
Purpose: Find VCPI conventional memory requirements.
CPU Mode: Real
Registers at Call: None
Return Registers: DX:AX = conventional memory requirements.
Details:
DX:AX is adjusted upward to an integer multiple of 16.
This routine simply loads DX:AX with a constant.

XLIBCONFIG (XLIB Configuration)
Purpose: Get XLIB configuration.
CPU Mode: Real
Registers at Call: None
Return Registers: AX = 0 if protected-mode structures are not initialized;
otherwise, AX = XLIB configuration. The value of lower nibble identifies the
protected-mode host/server. If 1 then DPMI is installed. If 2 then VCPI is
installed. If 3 then XLIB handles mode switches. Bit 4 is set if XMS is
installed.



























8






4. Mode Switching


As illustrated in Example 1, CALLPM may be used to transfer control to
32-bit protected mode in segment TSEG. When execution is returned to real
mode, segment registers, ESP, system flags, and control flags are restored to
their original values.
Execution may also be transferred to protected mode in TSEG with the
ENTERPM (enter protected mode) procedure. This procedure is specially
designed to accommodate mixed-language programming with high-level languages
operating in real mode. High-level language modules may be linked with
libraries containing protected-mode procedures. These procedures may then be
called from high-level code. However, such procedures must generally be
careful to restore register state. ENTERPM restores register state as
required by Microsoft high-level languages. In particular, ENTERPM restores
all registers except EAX, EDX, and the status flags. EAX and EDX are not
restored because these are typically used by high-level languages for return
values. ENTERPM otherwise functions exactly as CALLPM.
Both CALLPM and ENTERPM save register state as of call. CALLPM and
ENTERPM will also save and restore the state of the floating point unit (FPU)
if requested. FPU save/restore can be enabled by setting bit 2 of OFLAGS
(operation flags). The bit is clear by default. OFLAGS is a public WORD in
DSEG.
FPU state is saved with the FSAVE instruction executed from real mode.
This instruction resets the FPU; consequently, the FPU control word must be
redefined. XLIB will therefore load FPUCW (FPU control word) to the FPU
control word after performing FSAVE. FPUCW is a public WORD location in DSEG.
After entering protected mode through CALLPM or ENTERPM, control would
typically be returned to real mode with the RET instruction. However, control
may also be returned to real mode by jumping to either RETPM or EXITPM. These
are both procedures in TSEG. They will successfully return control to real
mode regardless of the stack state.
RETPM returns control to the real-mode caller of CALLPM/ENTERPM after
restoring segment registers, ESP, system flags, and control flags. EXITPM
returns control to the real mode caller after restoring all registers except
EAX, EDX, and the status flags.
The return address placed on the stack by CALLPM is actually a near
return to RETPM. Likewise, ENTERPM places a near return to EXITPM. CALLPM
and ENTERPM are otherwise identical procedures.
Once within protected mode, far procedures in real mode can be called
using CALLRM. CALLRM may be called only by protected-mode procedures in TSEG.
XLIB contains two hardware interrupt handlers that are typically
activated upon entry to protected mode. These handlers are fully explained in
Chapter 6. The first handler is hooked to the keyboard interrupt. This
handler manages hot key detection. The second handler is hooked to the FPU
interrupt and is designed to handle FPU exceptions.











9






Detailed Specifications


CALLPM (Call Protected Mode)
Purpose: Call a protected-mode procedure in TSEG with near return.
CPU Mode: Real
Registers at Call: SS:ESP = 32-bit protected-mode target offset.
Return Registers: Returns through RETPM. See RETPM for details.
Details:
All CPU registers except EAX and EDX are saved at locations presented in
Table 2. The stack is saved after the return address and argument have been
popped.
The target receives SS = TSEGDSEL with 1000H free bytes on the stack. The
return address on the stack is a near return to RETPM. The target receives by
default: DS = FLATDSEL, ES = TSEGDSEL, FS = DSEGSEL, and GS = DGROUPSEL.
Other registers, including the status flags, are received at values as of
call.
If bit 2 of OFLAGS is set, then the FPU state is also saved; the FPU is
initialized, and FPUCW is loaded to the control word.
XLIB hardware interrupt handlers are enabled (see Chapter 6). However, if
bit 1 of OFLAGS is set, then the XLIB interrupt handler for the FPU will not
be enabled.
If an FPU exception occurs after CALLPM, and if the FPU exception handler
is enabled, then protected mode will be exited through EXITPM rather than
RETPM. If FPU save/restore is not enabled, then real-mode will receive an
initialized FPU with control word set to the value existing as of the
exception.
The user may change the stack after the mode switch.
DS, ES, FS, and GS are actually loaded from: PMDS, PMES, PMFS, and PMGS.
These are public WORD locations in DSEG and are initialized to the default
selectors by INITXLIB. The user however may change these selectors to any
legal values after initialization.

RETPM (Return From Protected Mode)
Purpose: Return control to real mode with partial register restoration.
CPU Mode: Protected
Registers at Call: None
Return Registers: No return
Details:
RETPM switches to real mode and then restores all segment registers, ESP,
system flags, and control flags to values as of call to either CALLPM or
ENTERPM. XLIB hardware interrupt handlers are disabled (see Chapter 6).
Control is then transferred to the real-mode return address as of call to
CALLPM/ENTERPM.
If bit 2 of OFLAGS is set, then RETPM also restores FPU state.
RETPM will successfully execute regardless of stack state.

ENTERPM (Enter Protected Mode)
Purpose: Call a protected mode procedure in TSEG with near return.
CPU Mode: Real
Registers at Call: SS:ESP = 32-bit protected-mode target offset.
Return Registers: Returns through EXITPM. See EXITPM for details.
Details: This routine executes exactly as CALLPM except that a near return to
EXITPM is placed on the stack rather than to RETPM.



10







EXITPM (Exit Protected Mode)
Purpose: Return control to real mode with general register restoration.
CPU Mode: Protected
Registers at Call: None
Return Registers: No return
Details:
EXITPM switches to real mode and then restores all registers except EAX,
EDX, and status flags to values as of call to either CALLPM or ENTERPM. XLIB
hardware interrupt handlers are disabled (see Chapter 6). Control is then
transferred to the real-mode return address as of call to CALLPM/ENTERPM.
If bit 2 of OFLAGS is set, then EXITPM also restores FPU state.
EXITPM will successfully execute regardless of stack state.
The FPU exception handler performs a jump to EXITPM upon occurrence of any
unmasked FPU exception.


Table 2: CALLPM/ENTERPM Register Storage Locations by Public Symbol
-----------------------------------------------------------------------------
Register Symbol Symbol Type
-------- ------ -----------
EBX ORGEBX DWORD
ECX ORGECX DWORD
ESI ORGESI DWORD
EDI ORGEDI DWORD
EBP ORGEBP DWORD
ESP ORGESP DWORD
EFLAGS ORGEFLAGS DWORD
SS ORGSS WORD
DS ORGDS WORD
ES ORGES WORD
FS ORGFS WORD
GS ORGGS WORD
FPU State ORGFPU BYTE[94]
-----------------------------------------------------------------------------


CALLRM (Call a Real-Mode Procedure)
Purpose: Call a real-mode routine with far return from protected mode.
CPU Mode: Protected
Registers at Call: SS:ESP = far real-mode target address (four bytes).
Return Registers: All segment registers and ESP are restored. Other
registers, including status flags, are received with values as of the real-
mode RET instruction.
Details:
This is a near procedure in TSEG. It must therefore be called from TSEG.
Segment registers and ESP are saved at locations presented in Table 3. The
stack is saved after popping the return address and argument.
The target receives the XLIB real-mode stack (SS = DSEG) with 200H free
bytes. By default, the target receives DS = DGROUP, ES = DSEG, FS = DSEG, and
GS = DSEG. Other registers, including status flags, are received at values as
of call.
Code called by this routine should not perform calls to XLIB procedures
other than SWITCHPM and SWITCHRM (see Appendix G).



11






DS and ES are actually loaded from RMDS and RMES. These are public WORD
locations in DSEG and are initialized to the default values. However, the
user may change these values if desired.
This procedure does not change values in OFLAGS; consequently, XLIB
hardware interrupt handlers remain enabled in real mode if they were enabled
as of call (see Chapter 6).
FPU exceptions in real mode are handled the same as in protected mode;
however, system software is less apt to be left in regular state after real-
mode exceptions. FPU instructions should therefore be executed in protected
mode where possible.


Table 3: CALLRM Register Storage Locations by Public Symbol
-----------------------------------------------------------------------------
Register Symbol Symbol Type
-------- ------ -----------
ESP CALLESP DWORD
SS CALLSS WORD
DS CALLDS WORD
ES CALLES WORD
FS CALLFS WORD
GS CALLGS WORD
-----------------------------------------------------------------------------


































12






5. Inline Mode Switching


XLIB includes two routines to perform mode switching in C programs using
inline assembly. These procedures are very versatile and simple. A third
routine is included to facilitate calls from 16-bit segments to 32-bit near
procedures in TSEG.
Call INLINEPM to switch to protected mode. Descriptors are automatically
created for CS, SS, and DS. These registers are returned containing selectors
to the respective descriptors. ES is returned containing DSEGSEL. ES may be
used to load other XLIB selectors to segment registers.
Call INLINERM to return to real-mode. This function should be called
from the same segment as the previous call to INLINEPM and should be using the
same stack segment. INLINERM restores segment registers to values which
existed as of the call to INLINEPM.
The following Microsoft C 7.0 program illustrates the usage of these
procedures. The program contains a C subroutine called "getextmem" which uses
INLINEPM and INLINERM to retrieve a DWORD from extended memory.
Observe that since the inline assembly code is in a 16-bit segment,
prefixes must be used with 32-bit instructions. Also observe that INLINERM is
called indirect through a supplied pointer in DSEG called INLINERMPTR. This
is done to ensure that the intersegment call loads CS with the protected-mode
selector for CSEG (CSEGSEL) rather than with the segment constant.
This program may fail if an attempt is made to access logical addresses
which are either protected or undefined in the page tables.


Example 2: Using INLINEPM/INLINERM in C
-----------------------------------------------------------------------------
#include
#include
#define som _emit 0x66 /* switch operand mode */
#define sam _emit 0x67 /* switch address mode */
long __far getextmem(long address);

int goterr = 0; /* error flag */

main()
{
long l;
l = INITXLIB(); /* initialize XLIB */
if(l != 0)
{
printf("Library initialization error: %lX\n",l);
return 0;
}

l = getextmem(0x100000); /* read first address in 2ond meg */
if(goterr != 0)
{
printf("Inline mode-switch error: %lX\n",l);
return 0;
}
printf("%lX\n",l);
}



13







long __far getextmem(long address)
{
__asm
{
som ;mov eax,[bp+6]
mov ax,[bp+6] ; ""
call INLINEPM
jc error ;Error code in ax
mov ds,es:FLATDSEL
sam ;push dword ptr [eax]
som ; ""
_emit 0ffh ; ""
_emit 030h ; ""
pop ax
pop dx
call es:INLINERMPTR
jmp done
error:
xor dx,dx ;Return error code in dx:ax
inc goterr
done:
}
}
-----------------------------------------------------------------------------


Detailed Specifications


INLINEPM (Inline Protected-Mode)
Purpose: Return to real-mode caller in 16-bit protected mode.
CPU Mode: Real
Registers at Call: None
Return Registers:
CF clear if successful. Descriptors are created for CS, SS, and DS. These
descriptors have base addresses matching the calling contents of the
respective segment registers. The segment registers are returned containing
selectors to these descriptors. These selectors are also stored in DSEG at
the public WORD locations ILCSSEL, ILSSSEL, and ILDSSEL. See Chapter 2 for
further details as to descriptor specifications. ES, FS, and GS are returned
containing DSEGSEL. Protected mode receives other registers, except the
status flags, at values as of call.
CF set if unsuccessful. The processor will still be in real mode.
Unsuccessful execution can occur only under DPMI. EAX is returned with an
error code. AX = XLIB error code. The high word of EAX will equal a DPMI 1.0
error code (if provided by host). Other registers, except the status flags,
are unchanged.
Details:
INLINEPM stores segment registers at the same locations used by CALLPM and
ENTERPM (Table 1). INLINERM (see below) then restores these registers. C
will require that other registers be preserved also; however, the user is
responsible for managing these.




14






XLIB hardware interrupt handlers are not activated by this procedure (see
Chapter 6). The keyboard handler may be enabled by the user if hot key
detection is needful; however, the FPU exception handler should never be
enabled. Therefore, hot key detection should be enabled by setting both bits
0 and 1 in OFLAGS.
SP is preserved through the mode switch; however, the high word of ESP is
set to zero. The high word must be cleared since SS is set to a descriptor
having FFFFH limit and having its big bit set.
If multiple calls are made to INLINEPM with the same values in CS, SS, and
DS, then descriptors are created only on the first call. Subsequent calls
will therefore execute more quickly.

INLINERM (Inline Real-Mode)
Purpose: Return to 16-bit protected-mode caller in real mode.
CPU Mode: 16-bit protected mode
Registers at Call: CS and SS must equal values as of return from INLINEPM.
Return Registers: Segment registers are restored to values existing as of
former call to INLINEPM. Real mode receives other registers, except status
flags, at values as of call.
Details:
Since INLINERM will be called intersegment, caution must be taken that CS
is loaded with CSEGSEL and not CSEG. XLIB provides a far pointer to this
procedure called INLINERMPTR which may be used to execute the call.
INLINERM and CALL32 are the only 16-bit protected-mode procedures in XLIB.
They are also the only protected-mode procedures having far returns.

CALL32 (Call 32-bit Protected-Mode)
Purpose: Call a 32-bit protected-mode near procedure in segment TSEG from 16-
bit protected-mode.
CPU Mode: 16-bit protected mode
Registers at Call: SS:ESP = 32-bit protected-mode target offset.
Return Registers: All registers, including status flags, are returned with
values existing as of the 32-bit RET instruction.
Details:
CALL32 is a far procedure; therefore, caution must be taken that calls to
CALL32 load CS with CSEGSEL instead of CSEG. XLIB provides a far pointer to
this procedure in DSEG called CALL32PTR which may be used to execute the call.
CALL32 and INLINERM are the only 16-bit protected-mode procedures in XLIB.
They are also the only protected-mode procedures having far returns.
This procedure does not alter the state of OFLAGS.

















15






6. Interrupt Management


Interrupt management is the most complicated task performed by XLIB.
Accordingly, this chapter is the most difficult section of the user's manual.
In general, this chapter may be ignored for programs which do not install
interrupt handlers, do not require hot key detection, and do not use floating
point operations.


XLIB Interrupt Handlers


XLIB handles nearly all interrupts occurring in protected mode by
shifting to real mode and calling the currently installed real-mode interrupt
handlers. In all but three cases, XLIB uses inherited real-mode handlers.
XLIB installs its own handlers only for the DOS interrupt (INT 21H), the
keyboard interrupt (INT 9), and the FPU interrupt (INT 75H).
The DOS interrupt handler (INT 21H) intercepts all DOS calls and
determines if termination is requested (AH = 4CH). If not, then the interrupt
is immediately cascaded to DOS. If so, then XLIB performs housecleaning
before relaying the request to DOS. Under DPMI, XLIB will restore all
interrupt vectors and release all allocated descriptors. The DPMI host
automatically releases all allocated memory. Under other configurations, XLIB
will reset all real-mode interrupt vectors and release all allocated extended
memory.
INT 21H function 4CH may be executed in either real mode or protected
mode. DPMI hosts will expect this function to be executed from protected
mode; consequently, XLIB will switch to protected mode before relaying the
request to DPMI. XLIB also installs a protected-mode handler for INT 21H
under DPMI. This is to ensure that XLIB will see the termination request
before the DPMI host.
The keyboard interrupt handler is intended to facilitate termination of
protected-mode procedures with a keypress. When a user-defined hot key is
pressed, the keyboard interrupt handler will modify a flag variable. This
flag variable may then be polled periodically in the main thread of execution.
The keyboard interrupt handler is a real-mode routine.
The inconvenience of having to poll the flag variable is unfortunately
necessary. It is not generally safe to terminate within a hardware interrupt
handler, particularly in protected-mode environments. A hardware interrupt
generated by a keypress may have interrupted system software in the middle of
a system maintenance operation. Termination in such cases would likely leave
the system in an irregular and potentially unstable state.
Matters are further complicated in most protected-mode environments. For
example, a DPMI host will trap all hardware interrupts before cascading them
to interrupt handlers. If control is not returned to the host with the IRET
instruction, then the host will be left in an irregular state. Moreover, the
trapping procedure will likely switch stacks before relaying the interrupt to
the handler; consequently, the handler cannot determine the final return
address and therefore cannot change this address to a termination procedure.
These complications will nearly always occur when hardware interrupts are
virtualized. Virtual 8086 mode interrupts will almost certainly be
virtualized in either VCPI-based or DPMI-based environments.




16






When a key is pressed, the keyboard interrupt handler will examine the
key to determine if it is the hot key. If not, then the interrupt is cascaded
to the inherited real-mode handler. If so, then a hot key flag is recorded to
a DWORD whose linear address is recorded at CCODEPTR (condition code pointer).
The hot key is not cascaded. CCODEPTR is a public DWORD in DSEG. The hot key
flag is listed among XLIB error codes in Appendix B.
CCODEPTR initially contains the linear address to public DWORD location
CCODE which is in DSEG. Therefore, the hot key flag would be written to CCODE
by default. CCODEPTR may be changed by the user; however, it must point to an
address in conventional memory.
The hot key specification is stored in DSEG at a public WORD location
called HOTKEY. The lower byte of HOTKEY contains the scan code of the hot key
while the upper byte specifies the state of the shift keys. Bit 8 specifies
the state of SHIFT; bit 9 specifies CTRL, and bit 10 specifies ALT. All other
bits are ignored. Set bits require that the designated shift key be pressed.
The default setting for HOTKEY is zero. This setting disables hot key
detection since no key has a zero scan code.
The XLIB interrupt handler for FPU exceptions performs three major
functions upon the occurrence of any FPU exception that is unmasked in the FPU
control word. First, an error code is loaded to EAX and is also recorded at
the linear address in CCODEPTR. Next, the FPU is initialized with FNINIT (the
FPU control word is preserved). Third, control is transferred to EXITPM to
return to real mode. The high word stored in EAX and the condition code will
be the FPU status word. This word may be examined to determine the nature of
the exception. The FPU interrupt handler is a real-mode routine.
The response of the FPU to exception conditions is largely determined by
the settings in the FPU control word. If FPU save/restore is enabled, then
the FPU control word will be set to FPUCW upon execution of CALLPM/ENTERPM.
The default value for FPUCW is 0332H. This sets rounding control to nearest,
precision control to 64 bits, and unmasks exceptions for overflow, zero
divide, and invalid operations. Exceptions for underflow, precision, and
denormalized operations are masked, and are therefore handled internally by
the FPU. FPUCW may be modified by the user.
As explained above, the machine may be left in an unstable state after a
program has been terminated from within a hardware interrupt handler. This
can also be the case for the FPU interrupt handler. However, it will be safe
to continue execution after FPU exceptions under clean configurations. This
follows because interrupts are not virtualized and because the exception will
never be generated in the operating system (DOS does not use the FPU). Nor
should there be any problem with continuing execution after an exception in
protected-mode under VCPI. This follows since protected-mode interrupts
cannot be virtualized under VCPI. Exceptions occurring in Virtual 8086 mode
or under DPMI protected mode may however leave the machine in an irregular
state. Reboot may be necessary in these cases.
All tested DPMI hosts appear to be restored to normal state by execution
of INT 21H function 4CH (DOS termination). However, the DPMI specifications
offer no guarantees to this approach. FPU exceptions in virtual 8086 mode are
generally no problem in single-task environments; however, they will
frequently prove problematic under a multitasker such as DESQview.
Bit 0 of OFLAGS is used by XLIB to simultaneously enable or disable all
of its own hardware interrupt handlers. Setting the bit enables the handlers.
XLIB sets this bit upon calls to CALLPM/ENTERPM and clears the bit upon
return. All interrupts are immediately cascaded to the inherited real-mode
handlers when the bit is clear. Therefore, FPU exceptions are handled by XLIB



17






only after calls to CALLPM/ENTERPM. Accordingly, hot key detection is enabled
only after calls to CALLPM/ENTERPM. The user may set the bit under other
circumstances; however, XLIB will not be able to properly handle FPU
exceptions in these cases. The bit should be set by the user only when hot
key detection is desired and when it is certain that the FPU exception handler
will not be invoked.
The FPU exception handler may be permanently disabled by setting bit 1 in
OFLAGS. If this bit is set, then the FPU interrupt handler simply cascades
the interrupt to the inherited real-mode handler. XLIB will set the bit
during initialization if an FPU is not present; it is otherwise cleared.
Real-mode software interrupts which receive or return values in segment
registers cannot be used within protected mode because the deflection routine
will restore selectors to segment registers upon completion of the interrupt.
To use such software interrupts, one must switch to real mode through CALLRM;
issue the interrupt, and then transfer the segment registers to other
registers or to memory before returning to protected mode.
Certain software interrupts use status flags for return flags, typically
to signal error conditions. This is particularly the case for DOS interrupts.
The deflection routine will pass these alterations to the code which issued
the interrupt.


Installation of Interrupt Handlers


The user may install real-mode interrupt handlers in usual fashion. Such
handlers will also receive interrupts occurring in protected mode because
either XLIB or the DPMI host will deflect all protected-mode interrupts. If
XLIB deflects the interrupt, then the handler will receive SS = DSEG with ESP
set to 100H free bytes. Stack sizes under DPMI will depend upon the host, but
must contain a minimum of 200H free bytes to meet DPMI specifications.
The DOS routines to get and set interrupt vectors (INT 21H functions 35H
and 25H) receive and return values through segment registers; consequently,
they cannot be used in protected mode. Instead, use the XLIB procedures
PMGETRMIV and PMSETRMIV (protected mode - get/set real-mode interrupt vector).
The user may install a protected-mode interrupt handler from real mode by
calling SETPMIV (set protected-mode interrupt vector). The current protected-
mode interrupt vector may be obtained by calling GETPMIV.
From protected mode, interrupt vectors can be managed with PMSETPMIV and
PMGETPMIV. These procedures have the same specifications as SETPMIV and
GETPMIV.
Interrupt handlers installed with SETPMIV/PMSETPMIV are never disabled by
XLIB and will therefore always be active under protected-mode execution.
These handlers will not be active under real-mode execution in the absence of
DPMI. That is, real-mode interrupts are never deflected to protected-mode
handlers in such environments. However if DPMI is active, then all hardware
interrupts (IRQs 0-15) and the software interrupts: 1CH (BIOS timer tick),
23H (DOS CTRL C), and 24H (DOS critical error) are deflected from real mode to
the installed protected-mode handler. Therefore the protected-mode handler
always receives the interrupt first. If the handler cascades the interrupt,
then the real-mode handler will receive it next. If the user has not
installed protected-mode handlers for these interrupts, then they are serviced
by default handlers in the DPMI host. The default handlers typically deflect
the interrupts to the real-mode handlers.



18






If the programmer wishes to install a protected-mode interrupt handler
for a hardware interrupt or for INT 1CH, INT 23H, or INT 24H, then
consideration must be given to the fact that treatment of these interrupts
will differ under different protected-mode configurations. As noted above,
the DPMI host will always send these interrupts to the protected-mode handler
regardless of the CPU mode in which the interrupt occurred. If DPMI is not
installed, then protected-mode handlers receive control only under protected-
mode interrupts. Therefore, if the protected-mode handler is to receive real-
mode interrupts under such configurations, the programmer must install a real-
mode handler to perform the deflection.
XLIB includes a procedure called DEFLECTPM which can be used within a
real-mode interrupt handler to deflect control to a protected-mode handler.
DEFLECTPM functions only under VCPI and XLIB mode switching. If DPMI is
installed, then the procedure returns with no action. The intent of this
procedure is to enable simulation of DPMI treatment of hardware interrupts,
INT 1CH, INT 23H, and INT 24H.
Observe that if the real-mode handler deflects to the protected-mode
handler, then the latter should not cascade the interrupt since an infinite
loop would result. This follows because the initial protected-mode handler
deflects to the real-mode handler.
Were one to use DEFLECTPM in a real-mode handler for the interrupts named
above, then the protected-mode handler will receive all interrupts regardless
of the protected-mode configuration. This occurs naturally under DPMI.
DEFLECTPM ensures that it will occur under other configurations. Observe that
under DPMI, the real-mode handler will never be executed.
It is sometimes important that interrupt handlers execute in shortest
possible CPU time. This would typically be the case for handlers of the
communication ports. Since mode switching is time consuming, such handlers

should be installed for the CPU mode which is expected to receive the most
interrupts.
If DPMI is installed, then it is possible for multiple clients to operate
in a single virtual machine. In such cases, DPMI will always send hardware
interrupts to the primary client (the most recently installed client in the
virtual machine).
Under DPMI, all protected-mode handlers for hardware interrupts and
software interrupts 0-7 will receive control with interrupts disabled. Since
DPMI virtualizes the interrupt flag, the IRET instruction may not reenable
interrupts. Consequently, all handlers for these interrupts should execute
STI before executing IRET. Other protected-mode interrupts do not affect the
interrupt flag.
All real-mode interrupt handlers will receive control with interrupts
disabled regardless of the protected-mode configuration. All protected-mode
handlers will receive control with interrupts disabled under VCPI or XLIB mode
switching. However, if DPMI is installed, then protected-mode software
interrupts apart from 0-7 will receive the virtual interrupt flag at its value
as of the INT instruction. That is, DPMI does not alter the interrupt flag in
these cases.
Hardware interrupts IRQ 0 through IRQ 7 are typically assigned to
interrupt numbers 08H through 0FH, while IRQs 8 through 15 are typically
assigned interrupt numbers 70H through 77H. However, IRQs are remapped in
some operating environments, typically to facilitate exception handling. The
current mappings may be loaded from IRQ0INTNO (IRQ 0 interrupt number) and
IRQ8INTNO. These are public BYTE locations in DSEG. They should be read only
after the call to INITXLIB.



19






DESQview does remap hardware interrupts; however, its interrupt handlers
for the new locations generally transfer control to the addresses at the
conventional vectors. DESQview must be started with a command-line switch if
it is to accommodate certain hardware interrupts. In particular, the FPU
interrupt will not function properly under DESQview unless DESQview is started
with DV /HW:75:C.


Detailed Specifications


PMGETRMIV (Protected Mode - Get Real-Mode Interrupt Vector)
Purpose: Retrieve address of real-mode interrupt handler from protected mode.
CPU Mode: Protected
Registers at Call: AL = interrupt number.
Return Registers: Handler address returned in CX:DX.
Details: The DOS routine for this purpose (INT 21H function 35H) is not
useful because it returns a value in ES.

PMSETRMIV (Protected Mode - Set Real-Mode Interrupt Vector)
Purpose: Set address of real-mode interrupt handler from protected mode.
CPU Mode: Protected
Registers at Call: AL = interrupt number, CX:DX = address of handler.
Return Registers: None
Details:
The DOS routine for this purpose (INT 21H function 25H) is not useful
because it requires an argument in DS.
Real-mode interrupt handlers will also be called when interrupts occur in
protected mode provided that the protected-mode interrupt handler cascades the
interrupt. The default protected-mode handlers do in fact cascade all
interrupts.
XLIB never disables handlers installed by this procedure.

GETPMIV (Get Protected-Mode Interrupt Vector)
Purpose: Retrieve address of protected-mode interrupt handler from interrupt
descriptor table.
CPU Mode: Real
Registers at Call: AL = interrupt number.
Return Registers: Handler address returned in CX:EDX (CX is a selector).
Details: This routine does not return addresses of CPU exception handlers
under DPMI. Use DPMI functions directly for this purpose.

SETPMIV (Set Protected-Mode Interrupt Vector)
Purpose: Set address of protected-mode interrupt handler in interrupt
descriptor table.
CPU Mode: Real
Registers at Call: AL = interrupt number. Handler address in CX:EDX (CX is a
selector).
Return Registers: EAX = 0 if successful. EAX = error code if unsuccessful.
AX = XLIB error code. If DPMI is installed then the high word of EAX will
contain a DPMI 1.0 error code (if provided by host).
Details:
XLIB never disables handlers installed by this procedure.




20






Protected-mode interrupt handlers never receive interrupts occurring in
real mode unless DPMI is active. Under DPMI all hardware interrupts (IRQs 0-
15) and software interrupts 1CH, 23H, and 24H are deflected from real mode to
the installed protected-mode handler. The protected-mode handler therefore
receives control of the interrupt first. It may cascade the interrupt if so
desired, in which event, the real-mode handler receives the interrupt next. If
no protected-mode handler has been installed, then DPMI generally deflects the
interrupt to the real-mode handler.
All protected-mode handlers will receive control with interrupts disabled
unless DPMI is installed. Under DPMI, protected-mode software interrupts
apart from 0-7 do not alter the state of the virtual interrupt flag.
Protected-mode handlers under DPMI for hardware interrupts and software
interrupts 0-7 should execute STI before IRET to ensure that the virtual
interrupt flag is enabled.
If multiple DPMI clients are running in the same virtual machine, then the
primary client (most recently installed client) always receives hardware
interrupts.
If DPMI is installed, then protected-mode interrupt vectors should be reset
to original values before termination.
This routine should not be used to install CPU exception handlers under
DPMI. DPMI functions should be used for this purpose.

PMGETPMIV (Protected Mode - Get Protected-Mode Interrupt Vector)
Purpose: Retrieve address of protected-mode interrupt handler from interrupt
descriptor table.
CPU Mode: Protected
Details: This routine is the protected-mode version of GETPMIV. See GETPMIV
for specifications.

PMSETPMIV (Protected Mode - Set Protected-Mode Interrupt Vector)
Purpose: Set address of protected-mode interrupt handler in interrupt
descriptor table.
CPU Mode: Protected
Details: This routine is the protected-mode version of SETPMIV. See SETPMIV
for specifications.

DEFLECTPM (Deflect to Protected-Mode)
Purpose: Call a protected-mode interrupt handler.
CPU Mode: Real
Registers at Call: SS:ESP = interrupt number (2 bytes)
Return Registers: All registers except segment registers and ESP are returned
at values as of the protected-mode IRET instruction.
Details:
This procedure can be used only under VCPI and XLIB mode switching. It
returns with no action under DPMI. The routine is intended to simulate DPMI
treatment of hardware interrupts, INT 1CH, INT 23H, and INT24H. The routine
may be called from a real-mode interrupt handler to deflect the interrupt to
the protected-mode handler.
The protected-mode handler should not cascade the interrupt; otherwise, an
infinite loop will result.







21






7. Memory Management


XLIB supplies memory management procedures for both real and protected
modes. These procedures are configured at initialization to work with the
currently resident memory management interfaces.
Conventional memory may be allocated and released in real mode through
DOS in usual fashion (INT 21H functions 48H and 49H). However, DOS functions
may not work properly in protected mode. Therefore, use the XLIB routines
PMGETDOSMEM and PMFREEDOSMEM for such requests. PMFREEDOSMEM can also be used
to find the amount of available DOS memory.
The real-mode extended memory management procedures are GETMEM, FREEMEM,
and RESETMEM. GETMEM is used to allocate a block of extended memory. FREEMEM
may then be used to release the block. RESETMEM releases all previously
allocated blocks at once. GETMEM may also be used to find the amount of
available extended memory.
The protected-mode memory management procedures are PMGETMEM, PMFREEMEM,
and PMRESETMEM. These procedures function exactly as the corresponding real-
mode procedures: GETMEM, FREEMEM, and RESETMEM.
XLIB will seek extended memory through XMS only if it is present and if
both DPMI and VCPI are absent. If either protected-mode interface is present,
then all extended memory will be allocated through the configured interface.
XLIB will not use XMS to allocate memory from the high memory area (HMA)
or from upper memory blocks (UMBs). XLIB will however allocate from the HMA
when it has full responsibility for extended memory management (DPMI, VCPI,
and XMS are all absent). XLIB never issues calls under the Expanded Memory
Specification (EMS).


Detailed Specifications


PMGETDOSMEM (Protected Mode - Get DOS Memory)
Purpose: Allocate DOS memory block.
CPU Mode: Protected
Registers at Call: EAX = desired size of block in bytes.
Return Registers:
EAX = 0 if successful. A block handle is returned in EBX. The number of
allocated bytes is returned in ECX. The linear address of allocated block is
returned in EDX.
EAX = error code if unsuccessful. AX = XLIB error code. The high word of
EAX (EAH) will be set to a DOS error code. If DPMI is active, then EAH will
be a DPMI error code (codes are supplied by DPMI .9 and up).
Details:
The block will always be paragraph aligned and will have size equal to an
integer multiple of 16.
Call with EAX = 0 to get largest available DOS memory block (not total free
memory) in ECX (EAX, EBX, and EDX are preserved).
If DPMI is active, then the handle is actually a selector with base address
set to the linear address of the block. If DPMI is not active, then the
handle will be the segment of the block.
In real mode, DOS memory may be allocated directly from DOS (INT 21H
function 48H); however, this call will likely fail under DPMI protected mode.




22






PMFREEDOSMEM (Protected Mode - Free DOS Memory)
Purpose: Release previously allocated DOS memory block.
CPU Mode: Protected
Registers at Call: EAX = block handle.
Return Registers: EAX = 0 if successful; otherwise, EAX = error code. AX =
XLIB error. The high word of EAX (EAH) will be a DOS error code. If DPMI is
active, then EAH will equal a DPMI error code (codes are supplied by DPMI .9
and up).
Details: In real mode, DOS memory may be released directly by DOS (INT 21H
function 49H); however, this call will likely fail under DPMI protected mode.

GETMEM (Get Memory)
Purpose: Allocate extended memory block.
CPU Mode: Real
Registers at Call: EAX = desired size of block in bytes.
Return Registers:
EAX = 0 if successful. A block handle is returned in EBX. The number of
allocated bytes is returned in ECX. The logical address of allocated block is
returned in EDX.
EAX = error code if unsuccessful. AX = XLIB error code. If DPMI is
active, then the high word of EAX (EAH) will be a DPMI 1.0 error code (if
provided by host). If XMS is active, then EAH = XMS error code.
Details:
The page size for extended memory allocations is contained in PAGESIZE.
PAGESIZE is a DWORD in DSEG and should be read after initialization. The
blocks will have addresses that are PAGESIZE aligned and will have sizes equal
to an integer multiple of PAGESIZE. PAGESIZE will equal: 1024 for XMS, 4096
for VCPI, 4096 for most DPMI hosts, and 16 in the absence of a memory manager.
If XMS is present in conjunction with either DPMI or VCPI, no extended
memory will be requested through XMS. All extended memory will be requested
through the active protected-mode interface.
XMS is never used to allocate from the HMA or from UMBs. XLIB will however
allocate from the HMA in the absence of a memory management interface.
Call with EAX = 0 to get largest available extended memory block (not total
free memory) in ECX (EBX and EDX are preserved). This call can also return
with an error condition in EAX.

FREEMEM (Free Memory)
Purpose: Release previously allocated extended memory block.
CPU Mode: Real
Registers at Call: EAX = block handle.
Return Registers: EAX = 0 if successful; otherwise, EAX = error code. AX =
XLIB error code. If DPMI is active, then the high word of EAX (EAH) will be a
DPMI 1.0 error code (if provided by host). If XMS is active, then EAH = XMS
error code.
Details: FREEMEM does not release page tables allocated under VCPI. Call
RESETMEM for this purpose.

RESETMEM (Reset Memory)
Purpose: Release all previously allocated extended memory.
CPU Mode: Real
Registers at Call: None
Return Registers: EAX = 0 if successful; otherwise, EAX = error code. AX =
XLIB error code. If DPMI is active, then the high word of EAX (EAH) will be a



23






DPMI 1.0 error code (if provided by host). If XMS is active, then EAH = XMS
error code.
Details:
GETMEM will automatically allocate page tables as needed under VCPI.
RESETMEM will release such tables.
If DPMI is not installed, then RESETMEM will be called upon execution of
INT 21H function 4C (DOS termination). DPMI hosts reset extended memory
automatically.

PMGETMEM (Protected Mode - Get Memory)
Purpose: Allocate extended memory block while in protected mode.
CPU Mode: Protected
Details: This routine is the protected-mode version of GETMEM. See GETMEM
for specifications.

PMFREEMEM (Protected Mode - Free Memory)
Purpose: Free previously allocated extended memory block while in protected
mode.
CPU Mode: Protected
Details: This routine is the protected-mode version of FREEMEM. See FREEMEM
for specifications.

PMRESETMEM (Protected Mode - Reset Memory)
Purpose: Free all previously allocated extended memory while in protected
mode.
CPU Mode: Protected
Details: This routine is the protected-mode version of RESETMEM. See
RESETMEM for specifications.





























24






8. File Management


XLIB file management procedures are low-level routines with powerful
capabilities. These routines can load files to extended memory or save
extended memory to files. They can read and write files either sequentially
or randomly.
All XLIB file management routines will receive and return values in a
contiguous block of memory called a "file control block" (not to be confused
with DOS file control blocks). The file control block must be located in
conventional memory and must have the form presented in Table 4.


Table 4: XLIB File Control Block Structure
------------------------------------------------------------------------------
Field Name Field Type Field Description
---------- ---------- -----------------
CONDCODE DWORD Condition code from file operation
FNAME BYTE[68] File path and name (zero terminated string)
FHANDLE WORD File handle assigned by DOS
FPTRMODE WORD File pointer mode
FPTR DWORD File pointer
BLKADR DWORD Memory source/destination address
BLKSIZE DWORD Size of transfer block in bytes
BUFADR DWORD Buffer address (conventional memory address)
BUFSIZE WORD Buffer size in bytes
CONTROL WORD Control word
------------------------------------------------------------------------------

CONDCODE is used to return error codes. CONDCODE should be situated at
the starting address of the control block.
FNAME is a zero-terminated ASCII string defining the file path and name.
There cannot be more than 67 characters in this string, excluding the
termination character.
BLKADR and BLKSIZE define the source/destination memory block for the
transfer. This block may be in either conventional or extended memory.
BLKADR is a linear address.
XLIB uses DOS to access the disk. DOS cannot read or write to extended
memory; consequently, a conventional memory buffer must be set up for the DOS
transfers. File management routines shift to protected mode to perform
transfers between the buffer and the source/destination memory. BUFADR and
BUFSIZE define the conventional memory buffer. BUFADR is a linear address.
For fastest transfers, the memory block and the buffer should be DWORD
aligned and should have sizes equal to an integer multiple of 16.
FPTR and FPTRMODE specify the file pointer setting to be used before
transfers to or from the disk. FPTRMODE specifies how FPTR is to be
interpreted. The following values are valid for FPTRMODE:


FPTRMODE FPTR Interpretation
0 Unsigned offset from the beginning of the file
1 Signed offset from the current file pointer
2 Signed offset from the end of the file
3 FPTR is ignored. Use current file pointer (sequential mode)



25








CONTROL is not used in the present version of XLIB. Set all bits in
CONTROL to zero.
In assembly language or C, the file control block would typically be
defined by a structure. In BASIC, the file control block can be defined with
a user defined type.
Values are transferred to and from all file routines in EAX and in the
file control block. All routines should be called with the linear address of
the file control block in EAX. All routines return with two copies of the
error code; one in EAX and one in the condition code of the file control
block. A zero error code indicates successful execution.
Since these routines perform disk operations, special precautions should
be taken to ensure that parameters in the file control block are properly
defined before performing calls. In particular, one should always make sure
that the source/destination memory block and the conventional memory buffer
are properly defined. A safe rule is to simply set the buffer size to zero
because this forces XLIB to supply a buffer when opening or creating the file.


Detailed Specifications


XCREATE (Create File)
Purpose: Create and open a new file of specified name in specified directory.
CPU Mode: Real
Registers at Call: EAX = linear address of file control block.
Control Block at Call: FNAME = file path and name.
Return Registers: EAX = error code. AX = XLIB error code. If a DOS error
occurred, then the high word of EAX will be set to the DOS error code.
Control Block at Return: CONDCODE = error code. If CONDCODE = 0, then
FHANDLE = file handle assigned by DOS. If the procedure is called with
BUFSIZE = 0, then XLIB will set BUFADR and BUFSIZE to its own internal buffer.
Details:
If the file already exists, then it will be truncated to zero length.
The size and location of the internal buffer will depend upon how XLIB was
initialized. If DPMI is active, then the buffer will be slightly larger than
2K; otherwise, the buffer will be slightly larger than 1K. The linear address
and size of the buffer may be obtained from FILEBUFADR (DWORD), and
FILEBUFSIZE (WORD) in DSEG.
Files created by this routine will be given both read and write access.
This routine uses INT 21H function 3CH to create the file.

XOPEN (Open File)
Purpose: Open existing file of specified name in specified directory.
CPU Mode: Real
Registers at Call: EAX = linear address of file control block.
Control Block at Call: FNAME = file path and name.
Return Registers: EAX = error code. AX = XLIB error code. If a DOS error
occurred, then the high word of EAX will be set to the DOS error code.
Control Block at Return: CONDCODE = error code. If CONDCODE = 0, then
FHANDLE = file handle assigned by DOS. If the procedure is called with
BUFSIZE = 0, then XLIB will set BUFADR and BUFSIZE to its own internal buffer.
Details:



26






The file is opened for both read and write access.
The size and location of the internal buffer will depend upon how XLIB was
initialized. If DPMI is active, then the buffer will be slightly larger than
2K; otherwise, the buffer will be slightly larger than 1K. The linear address
and size of the buffer may be obtained from FILEBUFADR (DWORD), and
FILEBUFSIZE (WORD) in DSEG.
This routine uses INT 21H function 3DH to open the file.

XCLOSE (Close File)
Purpose: Close previously opened file.
CPU Mode: Real
Registers at Call: EAX = linear address of file control block.
Control Block at Call: FHANDLE = file handle.
Return Registers: EAX = error code. AX = XLIB error code. If a DOS error
occurred, then the high word of EAX will be set to the DOS error code.
Control Block at Return: CONDCODE = error code.
Details: This routine uses INT 21H function 3EH to close the file.

XSAVE (Save File)
Purpose: Create file with contents equal to specified memory block.
CPU Mode: Real
Registers at Call: EAX = linear address of file control block.
Control Block at Call: FNAME = file path and name. BLKADR/BLKSIZE = address
and size of memory block to provide file contents. BUFADR/BUFSIZE = address
and size of conventional memory buffer.
Return Registers: EAX = error code. AX = XLIB error code. If a DOS error
occurred, then the high word of EAX will be set to the DOS error code.
Control Block at Return: CONDCODE = error code.
Details:
The file cannot already be open. The file is both created and closed by
this routine.
This routine will replace any previously existing file named FNAME.
BLKADR/BLKSIZE may define a conventional memory block provided that this
block is not overlapped by BUFADR/BUFSIZE.
This routine transfers the source memory to the file through the buffer.
Transfers from buffer to disk are accomplished with INT 21H function 40H.

XLOAD (Load File)
Purpose: Load file contents to specified memory block.
CPU Mode: Real
Registers at Call: EAX = linear address of file control block.
Control Block at Call: FNAME = file path and name. BLKADR/BLKSIZE = address
and size of memory block to receive file contents. BUFADR/BUFSIZE = address
and size of conventional memory buffer.
Return Registers: EAX = error code. AX = XLIB error code. If a DOS error
occurred, then the high word of EAX will be set to the DOS error code.
Control Block at Return: CONDCODE = error code. If CONDCODE = 0, then
BLKSIZE = actual number of bytes transferred.
Details:
The file cannot already be open. The file is both opened and closed by
this routine.
The value of BLKSIZE as of call is interpreted as an upper limit on the
number of bytes to transfer. The entire file is loaded provided that it does
not contain more than BLKSIZE bytes.



27






BLKADR/BLKSIZE may define a conventional memory block provided that this
block is not overlapped by BUFADR/BUFSIZE.
This routine uses INT 21H function 3FH to transfer the disk contents to the
buffer. It then transfers the buffer contents to the destination memory.

XWRITE (Write to File)
Purpose: Write specified memory block to specified location in open file.
CPU Mode: Real
Registers at Call: EAX = linear address of file control block.
Control Block at Call: FHANDLE = file handle. FPTR/FPTRMODE = file pointer
setting for beginning of transfer. BLKADR/BLKSIZE = address and size of
memory block to provide file contents. BUFADR/BUFSIZE = address and size of
conventional memory buffer.
Return Registers: EAX = error code. AX = XLIB error code. If a DOS error
occurred, then the high word of EAX will be set to the DOS error code.
Control Block at Return: CONDCODE = error code.
Details:
The file must be opened with XOPEN or XCREATE before using this routine.
BLKADR/BLKSIZE may define a conventional memory block provided that this
block is not overlapped by BUFADR/BUFSIZE.
This routine uses INT 21H function 42H to set the file pointer. The source
memory is then transferred through the buffer to disk. Transfers from buffer
to disk are accomplished with INT 21H function 40H.
Sequential transfers should set FPTRMODE = 3 for fastest execution.

XREAD (Read From File)
Purpose: Write to specified memory block from specified location in open
file.
CPU Mode: Real
Registers at Call: EAX = linear address of file control block.
Control Block at Call: FHANDLE = file handle. FPTR/FPTRMODE = file pointer
setting for beginning of transfer. BLKADR/BLKSIZE = address and size of
memory block to receive file contents. BUFADR/BUFSIZE = address and size of
conventional memory buffer.
Return Registers: EAX = error code. AX = XLIB error code. If a DOS error
occurred, then the high word of EAX will be set to the DOS error code.
Control Block at Return: CONDCODE = error code.
Details:
The file must be opened with XOPEN or XCREATE before using this routine.
BLKADR/BLKSIZE may define a conventional memory block provided that this
block is not overlapped by BUFADR/BUFSIZE.
This routine uses INT 21H function 42H to set the file pointer. The file
contents are then transferred to the destination memory through the buffer.
The file contents are transferred to the buffer using INT 21H function 3FH.
Sequential transfers should set FPTRMODE = 3 for fastest execution.













28






9. Descriptor Management


All selectors in Table 1 up to DGROUPSEL are used by XLIB procedures;
consequently, the corresponding descriptors should never be changed. However,
descriptors for the other selectors may be modified. XLIB includes a
procedure called SETDESC (set descriptor) to facilitate such modifications. A
second routine called PMSETDESC is the protected-mode version of SETDESC.
Descriptors corresponding to the inline selectors should not be changed
in programs which also use the inline mode-switch procedures.


Detailed Specifications


SETDESC (Set Descriptor)
Purpose: Change a descriptor in the local descriptor table.
CPU Mode: Real
Registers at Call: BX = selector. EDX:EAX = the new descriptor.
Return Registers: EAX = error code. AX = XLIB error code. If DPMI is
installed, then the high word of EAX will equal a DPMI 1.0 error code (if
provided by host). EDX may be returned with some modifications to the access
rights bits.
Details:
The access rights bits in EDX will be edited before installation of the
descriptor. In particular: The application bit will be set to indicate an
application segment (rather than a system segment). Reserved bits will be
given proper settings. The descriptor privilege level will be set to the
appropriate value. If the descriptor corresponds to a code segment, then the
descriptor will also be marked as readable and nonconforming.
Segment registers which are loaded with the current value of the descriptor
will not necessarily be reloaded when the descriptor is changed.

PMSETDESC (Protected Mode - Set Descriptor)
Purpose: Change a descriptor in the local descriptor table.
CPU Mode: Protected
Details: This routine is the protected-mode version of SETDESC. See SETDESC
for details.



















29






10. Using XLIB in High-Level Language Libraries


The following program illustrates the usage of XLIB in libraries called
from Microsoft BASIC 7.0. The library contains a protected-mode procedure
which sums the elements in a single precision array created within BASIC. The
general methodology here is recommended for developing assembly language
libraries.
Since BASIC cannot call a 32-bit segment, a real-mode interface procedure
must be placed in a 16-bit segment to receive the BASIC call and then transfer
execution to 32-bit protected mode. The interface procedure is call SUMARRAY
while the 32-bit protected-mode procedure which actually sums the array
elements is called SUMARRAY32.
BASIC must pass certain arguments to the library procedures. These
include the array address and the number of elements to be summed. These
arguments could be passed on the stack; however, such approach proves awkward
since the stack must be changed when entering protected mode. Consequently,
BASIC places all arguments in a contiguous block of memory called a "control
block," and then passes only the address of the control block to the library.
BASIC constructs the control block with a user-defined type.
The first four bytes of the control block are reserved for placement of
error codes by the library procedures. Also, the address of the control block
is placed in CCODEPTR so that any FPU exceptions will be signalled in the
error code location as well. Were an FPU exception to occur, the FPU
interrupt handler will immediately transfer control to EXITPM which will shift
to real-mode, restore registers, and return control to the BASIC caller.
The library also contains a real-mode function called LINADR which may be
called by BASIC to convert segment addresses to linear addresses.
An example of this same program for Microsoft C 7.0 is included in
Appendix E.


Example 3: Calling Protected-Mode Libraries From BASIC
-----------------------------------------------------------------------------
+++++++++++++++++++++++++
+ ASSEMBLY CODE LIBRARY +
+++++++++++++++++++++++++


;The following library should be combined with XLIB.LIB using the Microsoft
;LINK and LIB utilities. If BASIC is to be executed from the QBX
;environment, then a quick library must be loaded with the environment. See
;BASIC documentation for instructions.


.MODEL LARGE,PASCAL
.386P

INCLUDE XLIB.INC








30






CSEG SEGMENT PARA PUBLIC USE16 'CODE'
ASSUME CS:CSEG, DS:DSEG

;Function to calculate linear address from segment address on stack.
;Returns linear address in DX:AX.
LINADR PROC FAR PUBLIC,
SEGADR:DWORD ;Segment address of variable
XOR EAX,EAX ;Clear high words
XOR EDX,EDX
MOV AX,WORD PTR SEGADR[0]
MOV DX,WORD PTR SEGADR[2]
SHL EDX,4 ;Calculate linear address
ADD EDX,EAX
MOV AX,DX
SHR EDX,16 ;Return linear address in DX:AX
RET
LINADR ENDP

;Structure defining control block for SUMARRAY.
ARRAYDATA STRUCT
CONDCODE DWORD 0 ;Condition code
N DWORD 0 ;Number of elements to sum
ADDRESS DWORD 0 ;Address of first element
SUM DWORD 0 ;Sum of array elements
ARRAYDATA ENDS

;Real-mode interface to SUMARRAY32. Segment address of control block having
;structure ARRAYDATA should be on the stack.
SUMARRAY PROC FAR PUBLIC,
CBSEGADR:DWORD ;Control block segment address
PUSH DS
PUSHW DSEG
POP DS
XOR EAX,EAX ;Clear high words
XOR EDX,EDX
MOV AX,WORD PTR CBSEGADR[2]
MOV DX,WORD PTR CBSEGADR[0]
SHL EAX,4 ;Calculate linear address
ADD EAX,EDX
MOV CCODEPTR,EAX ;Reset condition code address
POP DS ;Pop calling DS
PUSHD OFFSET SUMARRAY32
CALL ENTERPM ;Execute SUMARRAY32 in protected
RET
SUMARRAY ENDP

CSEG ENDS










31






TSEG SEGMENT PARA PUBLIC USE32 'CODE'
ASSUME CS:TSEG, SS:TSEG, DS:TSEG, ES:TSEG, FS:DSEG, GS:DGROUP

;Sum the elements of a single precision array. Array parameters are stored
;in a control block having structure of ARRAYDATA. The linear address of the
;control block is stored at CCODEPTR. An error code of -1 is returned in the
;condition code of the control block if the number of array elements is zero.
;XLIB places an error code in the control block if an FPU exception occurs
;while calculating the sum. This error code will have the FPU status word in
;the high word and the XLIB FPU error code in the low word. Observe that this
;routine will be called with DS = FLATDSEL (flat-model data descriptor) and
;FS = DSEGSEL (DSEG data descriptor).
SUMARRAY32 PROC NEAR
MOV EBX,FS:CCODEPTR ;Get control block
MOV EDX,ARRAYDATA.ADDRESS[EBX] ;Get array address
MOV ESI,ARRAYDATA.N[EBX] ;Get N
SUB ESI,1
JB NODATA ;Error: N = 0
FLDZ ;Initialize sum
SUMLOOP: FADD DWORD PTR [EDX+4*ESI]
SUB ESI,1
JAE SUMLOOP
FSTP ARRAYDATA.SUM[EBX] ;Save sum
RET
NODATA: MOV ARRAYDATA.CONDCODE[EBX],-1 ;Record error code
RET
SUMARRAY32 ENDP

TSEG ENDS
END


+++++++++++++++++++++
+ BASIC MAIN MODULE +
+++++++++++++++++++++


'The following Microsoft BASIC 7.0 program should be linked with the above
'library. The BASIC program first initializes XLIB. Next, it creates a
'single precision array. A control block for SUMARRAY is then constructed
'and the call to SUMARRAY is executed. Finally, the condition code in the
'control block is inspected and results are printed.

DEFINT A-Z

'Declare XLIB procedures
DECLARE FUNCTION XLIBMEMREQ& ()
DECLARE FUNCTION INITXLIB& ()
DECLARE FUNCTION XLIBCONFIG% ()

'Declare procedures in the library linked with XLIB
DECLARE FUNCTION LINADR& (SEG VARIABLE AS ANY)
DECLARE SUB SUMARRAY (SEG VARIABLE AS ANY)




32






'Structure for the control block
TYPE ARRAYDATA
CONDCODE AS LONG 'Location to receive any error codes
N AS LONG 'Number of elements to be summed
ADDRESS AS LONG 'Linear address of the array
SUM AS SINGLE 'Location for array sum
END TYPE

'Check XLIBCONFIG to see if XLIB has already been initialized. If not then
'call XLIBMEMREQ to find amount of conventional memory needed by XLIB and
'release at least this amount with the BASIC SETMEM function. XLIBMEMREQ
'returns with sign bit of DX set if an error occurred. The error is then
'identified by AX. XLIB will not be terminated upon completion of this
'program in the Microsoft QBX environment; therefore, initialization is
'required only once within the environment.
IF XLIBCONFIG = 0 THEN
TEMP& = XLIBMEMREQ
IF TEMP& >= 0& THEN
IF TEMP& > 0 THEN TEMP& = SETMEM(-TEMP& - 16&)
TEMP& = INITXLIB 'INITXLIB error code returned in TEMP&
ELSE
TEMP& = TEMP& AND &H7FFFFFFF 'Mask sign bit to leave error code only
END IF
IF TEMP& THEN
PRINT "Library initialization error: "; HEX$(TEMP&)
END
END IF
END IF

DIM A(100) AS SINGLE
DIM AD AS ARRAYDATA

FOR I = 0 TO 100 'Assign numbers to array
A(I) = I
NEXT I

AD.CONDCODE = 0& 'Clear the error code
AD.N = 50& 'Sum first 50 elements
AD.ADDRESS = LINADR(A(0)) 'Calculate and record linear address of A(0)

CALL SUMARRAY(AD)

IF AD.CONDCODE THEN
PRINT "Error: ";HEX$(AD.CONDCODE)
ELSE
PRINT "Sum: ";AD.SUM 'Should equal 1225
ENDIF

END
-----------------------------------------------------------------------------







33






Appendix A: Description of XLIB Public Data


The following is a summary of most public symbols located in the XLIB
data segment DSEG. This summary excludes the symbols presented in tables one
through three. All XLIB symbols conform to the PASCAL naming convention.

Symbol: CALL32PTR (CALL32 Pointer)
Symbol Type: DWORD
Default Setting: Far 16-bit protected-mode address of CALL32 procedure
Description: This location is a pointer to the CALL32 procedure and is
included to facilitate intersegment calls. The contents of the location
should not be changed.

Symbols: CCODEPTR/CCODE (Condition Code Pointer/Condition Code)
Symbol Types: DWORD/DWORD
Default Settings: CCODEPTR = linear address of CCODE. CCODE = 0.
Descriptions: XLIB interrupt handlers will place flags in the condition code
to signal the occurrence of the interrupt. Flags are placed for FPU
exceptions and hot key presses. CCODEPTR initially contains the linear
address of CCODE. CCODEPTR may be changed by the user, but must point to a
DWORD in conventional memory. The user is responsible for initializing the
condition code.

Symbols: CSEGVAL, TSEGVAL, DSEGVAL, DGROUPVAL (Segment Values)
Symbol Types: WORD
Default Settings: CSEG, TSEG, DSEG, DGROUP
Descriptions: These are memory locations initialized to the respective
segment values. Code in TSEG should not contain segment constants since DOS
may not be able to handle them in relocation edits. Read these locations to
get segment values. User segments should be handled the same way. These
locations should not be changed.

Symbols: FILEBUFADR/FILEBUFSIZE (File Buffer Specifications)
Symbol Types: DWORD/WORD
Default Settings: Varies with operating environment
Descriptions: FILEBUFADR contains the linear address of the internal file
buffer in XLIB. FILEBUFSIZE contains the size of the buffer in bytes. This
buffer is used only by the file management routines. The size and location of
the buffer will depend upon the operating environment. If DPMI is active,
then the buffer will be slightly larger than 2K; it is otherwise slightly
larger than 1K. These location should be read only after initialization.

Symbol: FPUCW (Floating Point Unit Control Word)
Symbol Type: WORD
Default Setting: 0332H
Description: FPUCW is optionally loaded to the FPU control word by CALLPM and
ENTERPM. The default sets rounding control to nearest, precision control to
64 bits, and unmasks exceptions for overflow, zero divide, and invalid
operations. Exceptions for underflow, precision, and denormalized operations
are masked, and are therefore handled internally by the FPU. Set bit 2 of
OFLAGS to enable FPU save/restore and load of FPUCW.





34






Symbol: HOTKEY (Hot Key)
Symbol Type: WORD
Default Setting: 0H
Description: HOTKEY specifies the hot key for the keyboard interrupt handler.
The low byte of HOTKEY specifies the scan code for the key. The upper byte
specifies the state of the shift keys. Bit 8 specifies SHIFT; bit 9 specifies
CTLR, and bit 10 specifies ALT. Set bits mean that the designated key must be
pressed. All other bits are ignored. When the hot key is pressed, the XLIB
keyboard interrupt handler will record the hot key flag at the DWORD whose
linear address is stored at CCODEPTR. The default setting for HOTKEY is 0.
This setting effectively disables hot key detection since no key has a zero
scan code.

Symbol: IFLAGS (Initialization Flags)
Symbol Type: WORD
Default Setting: 0
Description: IFLAGS is used by INITXLIB to control the initialization
process. Bit 0 of IFLAGS determines DPMI/VCPI priority in the event that both
interfaces are present. If this bit is clear then DPMI will be installed in
such cases. The other bits are reserved.

Symbol: INLINERMPTR (INLINERM Pointer)
Symbol Type: DWORD
Default Setting: Far 16-bit protected-mode address of INLINERM procedure
Description: This location is a pointer to the INLINERM procedure and is
included to facilitate intersegment calls. The contents of the location
should not be changed.

Symbols: IRQ0INTNO/IRQ8INTNO (IRQ X Interrupt Number)
Symbol Types: BYTE/BYTE
Default Settings: Varies with operating environment
Descriptions: Specifies the interrupt number assigned to IRQ X. IRQs 0
through 7 and IRQs 8 through 15 are assigned to contiguous interrupt numbers.
These locations are valid only after call to INITXLIB. Typically, IRQ 0 is
assigned to interrupt 8, and IRQ 8 is assigned to interrupt 70H; however,
these assignments may have been changed by system software.

Symbol: OFLAGS (Operation Flags)
Symbol Type: WORD
Default Setting: Varies with operating environment
Description: OFLAGS controls post-initialization operation of XLIB.
Setting bit 0 enables XLIB hardware interrupt handlers. These handlers
will continue to receive interrupts but will always cascade them when the bit
is clear. XLIB sets this bit only at calls to CALLPM and ENTERPM and then
clears the bit upon return. When the bit is clear, hot key detection is
disabled, and the XLIB FPU interrupt handler is disabled.
Setting bit 1 causes all FPU interrupts to be cascaded to the inherited
real-mode interrupt handler. This bit is initialized by INITXLIB. It is set
if no FPU is present; it is otherwise cleared.
Setting bit 2 enables FPU save/restore in CALLPM and ENTERPM. Setting this
bit also causes load of FPUCW to the FPU control word. The bit is clear by
default.





35






Symbol: PAGESIZE (Page Size)
Symbol Type: DWORD
Default Setting: Varies with operating environment
Description: This memory location contains the minimum unit (in bytes) for
extended memory allocation. PAGESIZE is initialized by INITXLIB. It will
contain 4096 for VCPI, 1024 for XMS, and 16 for clean configurations. Values
can vary under DPMI but will typically equal 4096. Extended memory requests
are rounded up to the nearest integer multiple of PAGESIZE. Extended memory
blocks will be PAGESIZE aligned.

Symbols: PMDS, PMES, PMFS, PMGS (Protected-Mode Segments)
Symbol Types: WORD
Default Settings: FLATDSEL, TSEGDSEL, DSEGSEL, DGROUPSEL
Descriptions: These memory locations are loaded to data segment registers by
CALLPM and ENTERPM before transferring control to the protected-mode target.
These locations are respectively loaded to DS, ES, FS, and GS. The contents
of these locations may be changed to any legal selectors after the call to
INITXLIB.

Symbols: RMDS, RMES (Real-Mode Segments)
Symbol Types: WORD
Default Settings: DGROUP, DSEG
Descriptions: These memory locations are loaded to data segment registers by
CALLRM before transferring control to the real-mode target. These locations
are respectively loaded to DS and ES. The contents of these locations may be
changed if desired.































36






Appendix B: XLIB Error Codes


XLIB error codes are always returned in AX. In many cases, the high word
of EAX will be returned with specific information about the error, such as
XMS, DPMI, or DOS error codes.
Although error codes are not provided in the DPMI .9 specification, many
DPMI .9 hosts do return DPMI 1.0 error codes. DPMI 1.0 error codes may in
fact be DOS error codes returned to the DPMI host by DOS. If the sign bit
(bit 15) of the error code is clear, then the error code was issued by DOS.


Condition Code Flags
01H FPU exception
02H Hot key pressed

General Errors
10H Interface not available
11H Unable to identify operating environment
12H DOS memory allocation failure
13H DOS memory release error
14H Failed to enable A20
15H Insufficient logical address space
16H Insufficient number of extended memory block handles
17H Bad extended memory block handle
18H Bad selector
19H Unable to create file
1AH Unable to open file
1BH Unable to read file
1CH Unable to write file
1DH Unable to set file pointer
1EH Unable to close file
1FH Disk full

Errors Occurring Under DPMI (See Appendix C for codes returned by DPMI)
20H Protected mode initialization failure
21H Descriptor allocation error
22H Descriptor installation error
23H Unable to switch protected mode interrupt vector
24H Insufficient extended memory error
25H Extended memory allocation error
26H Extended memory release error
27H DPMI DOS memory allocation error
28H DPMI DOS memory release error
29H Unable to set descriptor base address

Errors Occurring Under XMS (See Appendix D for codes returned by XMS)
30H Unable to measure available extended memory
31H Insufficient extended memory error
32H Extended memory allocation error
33H Unable to lock extended memory
34H Unable to unlock extended memory
35H Extended memory release error




37






Errors Occurring Under VCPI
40H Error in determining protected mode entry point
41H Unable to determine physical address of DOS memory page
42H Unable to determine hardware interrupt mappings
43H Insufficient extended memory error
44H Unable to determine number of free extended memory pages
45H Extended memory allocation error
46H Extended memory release error

















































38






Appendix C: DPMI 1.0 Error Codes


DPMI 1.0 error codes may in fact be DOS error codes returned to the DPMI
host by DOS. If the sign bit (bit 15) of the error code is clear, then the
error code was issued by DOS.


Number Explanation
8001H Unsupported function
8002H Invalid state for requested operation
8003H System integrity would be endangered
8004H Deadlock situation detected by host
8005H Serialization request cancelled
8010H Resource unavailable
8011H Host unable to allocate descriptor
8012H Linear memory unavailable
8013H Physical memory unavailable
8014H Backing store unavailable
8015H Callback specifications cannot be allocated
8016H Cannot allocate handle
8017H Lock count limits exceeded
8018H Resource owned exclusively by another client
8019H Resource already shared by another client
8021H Invalid value
8022H Invalid selector
8023H Invalid handle
8024H Invalid callback
8025H Invalid linear address
8026H Request not supported by hardware



























39






Appendix D: XMS Error Codes


Number Explanation
80H Function not implemented
81H VDISK was detected
82H An A20 error occurred
8EH General driver error
8FH Unrecoverable driver error
90H HMA does not exist
91H HMA is already in use
92H Attempt to allocate less than HMAMIN of HMA
93H HMA is not allocated
94H A20 is still enabled
A0H All extended memory is allocated
A1H All available handles are allocated
A2H Invalid handle
A3H Source handle is invalid
A4H Source offset is invalid
A5H Destination handle is invalid
A6H Destination offset is invalid
A7H Length is invalid
A8H Move has an invalid overlap
A9H Parity error
AAH Block is not locked
ABH Block is locked
ACH Block lock count overflow
ADH Lock failed
B0H Only a smaller upper memory block (UMB) is available
B1H No UMB's are available
B2H UMB segment number is invalid


























40






Appendix E: Calling Protected-Mode Libraries From C


This appendix contains a C version of the BASIC program presented in
Example 3. Microsoft C version 7.0 is used to create a float array. C then
calls a protected-mode assembly language procedure in a library to sum the
elements of the array. The assembly language library is presented in Example
3. C calls a 16-bit procedure called SUMARRAY This procedure then transfers
control to a 32-bit protected-mode procedure called SUMARRAY32. The latter
procedure performs the actual calculations. Parameters defining the array are
placed in a contiguous block of memory defined by a C structure. C passes the
address of this structure to the library. The first four bytes in the
structure are reserved for error codes. The linear address of the structure
is placed in CCODEPTR so that any FPU exceptions will be recorded by the FPU
interrupt handler in the error code location. The SUMARRAY32 procedure will
also record an error if the parameter defining the number of elements to be
summed is zero.
The C code is somewhat simpler than the corresponding BASIC code because
DOS memory does not have to be released prior to calling INITXLIB. This
follows because C does not claim all DOS memory as does BASIC.
C is more powerful than BASIC in that it can access data under external
symbols whereas BASIC cannot. Access to XLIB public data is made possible in
C by including the header file called XLIB.H. This file makes all XLIB public
data and public real-mode procedures visible to C. It also contains
declarations which adapt the PASCAL conventions of XLIB.


-----------------------------------------------------------------------------
+++++++++++++++++++++
+ C MAIN MODULE +
+++++++++++++++++++++


/* The following Microsoft C 7.0 program should be linked with the assembly
language library in Example 3. Combine the library with XLIB.LIB using the
Microsoft LIB utility. The C program first initializes XLIB. Next, it
creates a float array. A control block for SUMARRAY is then constructed
and the call to SUMARRAY is executed. Finally, the condition code in the
control block is inspected and results are printed. */

#include
#include

extern long __far __pascal LINADR(void __far *ptr);
extern void __far __pascal SUMARRAY(void __far *ptr);

struct arraydata
{
long condcode;
long n;
long address;
float sum;
} ad;




41






float a[101];

main()
{
int i;
long temp;

temp = INITXLIB();
if (temp != 0)
{
printf("Initialization Error: %lX\n",temp);
return 0;
}

for(i = 0; i <= 100; i++)
a[i] = i;

ad.condcode = 0;
ad.n = 50;
ad.address = LINADR(a);

SUMARRAY(&ad);
if (ad.condcode != 0)
{
printf("Error: %lX\n",ad.condcode);
return 0;
}
printf("Sum: %f\n",ad.sum);
}
-----------------------------------------------------------------------------



























42






Appendix F: Reporting Problems With XLIB


All efforts to isolate and report problems with XLIB will be appreciated.
The following steps will greatly facilitate bug-tracking:

1) Ensure that your own program always checks the error codes returned by XLIB
procedures. These codes will likely resolve the problem. If not, then make
note of the code.

2) Attempt to execute your program under DPMI, VCPI, and in the absence of
both. If the problem relates to memory management, then also attempt to
execute your program in the presence of XMS but in the absence of DPMI and
VCPI, then attempt to execute in the absence of all three interfaces. It will
generally be found that the problem occurs only under a specific interface.
If so, then note the interface under which the problem occurs.

3) If the problem occurs only under one interface, then attempt to execute
your program under different implementations of the interface. For example, a
DPMI host is contained in Windows 3.1, 386MAX, QDPMI, and OS/2 2.x. Try
executing your program under each host and make note of the results. Problems
occurring only under one host are generally indicative of bugs in the host
rather than XLIB.

4) Try different options on your compiler, assembler, and linker. It is
sometimes the case that code is not processed properly under some options.

5) Report problems to:

Dr. David Pyles
TechniLib Company
P.O. Box 6818
Jackson, Ms. 39282
(601) 372-7433























43






Appendix G: The SWITCHPM and SWITCHRM Procedures


SWITCHPM and SWITCHRM are the primitive mode-switch routines used by
nearly all XLIB procedures requiring execution in both real and protected
modes. They are made public for users who need to perform mode switching
tasks not provided by XLIB. These routines do not conform to the general
conventions followed by other XLIB procedures; consequently, they are
presented in an appendix.
There are special situations in which CALLPM and ENTERPM may not be
suitable for performing mode switches. For example, suppose one has a
protected-mode hardware interrupt handler that is intended to service
interrupts occurring in both real and protected modes. Unless DPMI is
installed, the user will have to write a second handler to deflect real-mode
interrupts to the protected-mode handler. CALLPM and ENTERPM could not be
used in the event that the interrupt occurred in a procedure called from
protected mode will CALLRM. This follows because CALLPM and ENTERPM are not
reentrant. Instead, the user must perform the mode switches either with
DEFLECTPM or with SWITCHPM and SWITCHRM. A second example could occur where
the user needs to perform mode switches without the overhead of CALLPM and
ENTERPM. SWITCHPM and SWITCHRM perform mode switches in minimum CPU time.
Both SWITCHPM and SWITCHRM are near procedures in CSEG; therefore, they
must be called from this segment. SWITCHPM returns to the caller in 16-bit
protected mode. SWITCHRM returns to the caller in real mode. Both procedures
must be called with a stack in DSEG. Do not use XLIB stacks for this purpose.
SWITCHPM returns with CS = CSEGSEL and with all other segments equal to
DSEGSEL. All other registers, except the status flags, are preserved.
SWITCHRM returns with CS = CSEG and with SS, DS, and ES set to DSEG. FS
and GS are undefined. All other registers, except the status flags, are
preserved.



























44