XLIB PROGRAMMER'S MANUAL VERSION 4.1 (DOS Extender Library) TechniLib Company Copyright 1993 1994, 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 $40.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 1994, 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 7 4. Mode Switching 10 5. Inline Mode Switching 14 6. Interrupt Management 18 7. Memory Management 25 8. File Management 29 9. Descriptor Management 34 10. Using XLIB in High-Level Language Libraries 35 11. Using XLIBE for Debugging and Exception Trapping 39 TABLES Page 1. XLIB Segments and Selectors by Public Symbol 5 2. IFLAGS Settings for Memory Allocation Control 8 3. CALLPM/ENTERPM Register Storage Locations by Public Symbol 12 4. CALLRM Register Storage Locations by Public Symbol 13 5. XLIB File Control Block Structure 29 EXAMPLES Page 1. Simple Mode Switching Under XLIB 3 2. Using INLINEPM/INLINERM in C 15 3. Calling Protected-Mode Libraries From BASIC 35 APPENDICES Page A. Description of XLIB Public Data 43 B. XLIB Error Codes 47 C. DPMI 1.0 Error Codes 49 D. XMS Error Codes 50 E. Calling Protected-Mode Libraries From C 51 F. Technical Support 53 G. The SWITCHPM and SWITCHRM Procedures 54 H. Debugging 55 iii INDEX TO PROCEDURE SPECIFICATIONS Initialization Routines Name Purpose CPU Mode Page INITXLIB Initialize XLIB Real 8 XLIBMEMREQ Get XLIB convention memory requirements Real 9 DPMIMEMREQ Get DPMI conventional memory requirements Real 9 VCPIMEMREQ Get VCPI conventional memory requirements Real 9 XLIBCONFIG Get XLIB post-initialization configuration Real 9 Mode Switch Routines Name Purpose CPU Mode Page CALLPM Call protected-mode procedure Real 11 RETPM Return from protected mode to real mode Protected 11 ENTERPM Call protected mode procedure Real 11 EXITPM Return from protected mode to real mode Protected 12 CALLRM Call real-mode procedure from protected mode Protected 12 Inline Mode Switch Routines Name Purpose CPU Mode Page INLINEPM Return to caller in protected mode Real 16 INLINERM Return to caller in real mode Protected 16 CALL32 Call 32-bit procedure from 16-bit protected mode Protected 17 Interrupt Management Routines Name Purpose CPU Mode Page PMGETRMIV Get real-mode interrupt vector Protected 22 PMSETRMIV Set real-mode interrupt vector Protected 22 GETPMIV Get protected-mode interrupt vector Real 23 SETPMIV Set protected-mode interrupt vector Real 23 PMGETPMIV Get protected-mode interrupt vector Protected 23 PMSETPMIV Set protected mode interrupt vector Protected 24 DEFLECTPM Deflect real-mode interrupt to protected mode Real 24 SWITCHPM Perform nestable switch to protected mode Real 54 SWITCHRM Perform nestable switch to real mode Real 54 Memory Management Routines Name Purpose CPU Mode Page PMGETDOSMEM Allocate DOS memory block Protected 25 PMFREEDOSMEM Release DOS memory block Protected 26 GETMEM Allocate extended memory block Real 26 FREEMEM Release extended memory block Real 27 RESETMEM Release all extended memory blocks Real 27 PMGETMEM Allocate extended memory block Protected 27 PMFREEMEM Release extended memory block Protected 27 PMRESETMEM Release all extended memory blocks Protected 27 MAPIO Get logical address for memory-mapped IO device Real 28 PMMAPIO Get logical address for memory-mapped IO device Protected 28 iv INDEX TO PROCEDURE SPECIFICATIONS (CONTINUED) File Management Routines Name Purpose CPU Mode Page XCREATE Create file Real 30 XOPEN Open existing file Real 30 XCLOSE Close file Real 31 XSAVE Save memory to created file Real 31 XLOAD Load entire file to memory Real 31 XWRITE Random write from memory to file Real 32 XREAD Random read from file to memory Real 32 PMXCREATE Create file Protected 33 PMXOPEN Open existing file Protected 33 PMXCLOSE Close file Protected 33 PMXSAVE Save memory to created file Protected 33 PMXLOAD Load entire file to memory Protected 33 PMXWRITE Random write from memory to file Protected 33 PMXREAD Random read from file to memory Protected 33 Descriptor Management Routines Name Purpose CPU Mode Page SETDESC Place descriptor in local descriptor table Real 34 PMSETDESC Place descriptor in local descriptor table Protected 34 Debugging Routines (XLIBE only) Name Purpose CPU Mode Page SETWATCH Set debug data watchpoint Real 41 FREEWATCH Release debug data watchpoint Real 41 RESETWATCH Release all debug data watchpoints Real 42 PMSETWATCH Set debug data watchpoint Protected 42 PMFREEWATCH Release debug data watchpoint Protected 42 PMRESETWATCH Release all debug data watchpoints Protected 42 v 1. Introduction XLIB is an assembly language library which greatly simplifies protected- mode programming under Microsoft DOS. Assembly language programmers will likely abandon real-mode programming after acquiring XLIB. With only two calls to XLIB procedures, assembly language programs can be utilizing the simplicity and power of 32-bit processing. C and C++ programs can harness the powers of 16-bit protected mode using inline assembly. Additionally, the XLIB archive contains a second library call EASYX which can be used by all high- level languages to gain access to extended memory. 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. The archive for XLIB includes libraries and sample code in both Microsoft and Borland formats. XLIB has been successfully implemented with several Borland and Microsoft languages. The archive for version 4.1 provides a second library called XLIBE. This library contains all of the procedures in XLIB plus other procedures designed for debugging and exception trapping. XLIBE is capable of trapping exceptions in real mode also; therefore, it is potentially useful even to programmers who would otherwise have no interest in protected mode. 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. There is little doubt that microcomputer programming is headed for a protected-mode future. However, it is not apparent at present which of these operating systems will prevail as the future leader. DOS remains the dominant system for the time being, and this may remain the case for longer than many anticipate because DOS is the common thread which links all of these systems. All of these systems have DOS emulation capabilities. Extended DOS programs will almost equal the native programs of these systems in power, and will far surpass them in compatibility. Future programs will likely operate exclusively in protected mode using the flat (unsegmented) 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 1 to execute genuine flat-model code. XLIB does not load and relocate such code; however, it does provide 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). This versatility makes XLIB compatible with nearly all common operating environments for 386, 486, and Pentium processors. XMS is the standard interface for managing extended memory. HIMEM.SYS by Microsoft is the most commonly known XMS driver. VCPI is an older protected- mode interface designed to coordinate possibly several protected-mode programs. VCPI is an optional subset of EMS (Expanded Memory Specification). The most common VCPI driver is Microsoft's EMM386.SYS. DPMI is the protected- mode interface in Microsoft Windows and is considered the interface of the future (though VCPI remains popular). The more recent versions of Qualitas' 386MAX (version 6 and up) contain all of the above interfaces. Quarterdeck's QEMM386 contains XMS and VCPI. Quarterdeck supplies DPMI in a second driver called QDPMI. The latter is supplied free of charge but requires QEMM386. 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. This manual assumes the reader to have some degree of familiarity with protected mode and a working knowledge of assembly language. If you are not familiar with protected mode, then read the tutorial in DONTREAD.ME. This tutorial is quite informative. Many readers will be able to bypass this manual altogether after working through the tutorial. Those unfamiliar with assembly language should consider using the EASYX library. This library is a real-mode interface to XLIB. It contains procedures which may be utilized without assembly language. The documentation for EASYX is included in the XLIB archive. The reader might wish to refer to this documentation now; however, the present document may still be needed for technical reference. 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 for special circumstances. 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 (initialize XLIB). 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 complications apart from a few calls to XLIB extended memory management procedures. To achieve the simplicity of the above program, XLIB must handle numerous complexities internally. This manual attempts to give the reader thorough insight into the workings of XLIB. It also discusses at length the numerous configuration options that are available to the programmer. 3 2. XLIB Conventions and Structure The XLIB archive contains files for both Microsoft and Borland languages. The files for Borland have the same names as those for Microsoft except a "B" is added to the base name. For example, XLIB.LIB is the XLIB library intended for Microsoft languages whereas XLIBB.LIB is intended for Borland languages. Sample code in this document is always for Microsoft languages; however, Borland versions may be found in the archive. Important information about using XLIB with Microsoft and Borland languages has been included in the README.DOC file. All instructions pertaining to XLIB.LIB also pertain to XLIBE.LIB; however, the latter library contains additional procedures for debugging and for trapping processor exceptions. XLIBE is intended for program development while XLIB is intended for release versions. The additional features in XLIBE are discussed in Chapter 11. The Microsoft version of XLIB was developed and tested under Microsoft DOS version 6.1 using Microsoft Assembler (MASM) version 6.11, Microsoft LINK version 5.31.009, and Microsoft LIB version 3.20.01. The Borland version of XLIB was developed and tested using Turbo Assembler (TASM) version 4.0, TLINK version 6.0, and TLIB version 4.0. XLIB has been tested under Microsoft Windows 3.1, Qualitas 386MAX versions 6.02 and 7.0, Quarterdeck QEMM386 versions 6.02 and 7.03, Quarterdeck QDPMI versions 1.01 and 1.03, Quarterdeck DESQview version 2.42, and IBM OS/2 versions 2.0 and 2.1. 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 (exceptions are noted in Appendix G). 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 4 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. TASM programmers should use the LARGESTACK directive for code in TSEG. Without this directive, stack frames will be set up with BP and SP rather than EBP and ESP. The results could be disastrous if the high words of the 32-bit registers are nonzero, as could be the case if TSEG exceeds 64K. All XLIB procedures may be called with interrupts enabled. An enabled interrupt state will never be returned disabled. With one exception, all XLIB public data is contained in a 16-bit segment called DSEG. The user may also place data in DSEG but is seldom required to do so. XLIB uses the PASCAL calling and naming convention. The PASCAL convention is equivalent to the BASIC and FORTRAN conventions. 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. 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 _____________________________________________________________________________ 5 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 expand-up and writable. Data descriptors also 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. Therefore, the user should not assume these values to be constant. Since selector values 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 one 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 (see Chapter 9). Selector values however should not be changed. 6 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 possibly attempt to allocate some memory through DOS. Since high-level language modules and assembly language modules often claim all available conventional memory, INITXLIB may fail for lack of available memory. This problem can be averted with MASM programs by linking with the CPARM:1 parameter. This forces the program to claim no more conventional memory than is necessary. TASM programs and some high-level language programs should release memory to DOS during initialization. This process is illustrated for TASM in the file EXAMP1B.ASM. In some cases, the main program will need to retain all memory except what is necessary for XLIB. The XLIB procedure XLIBMEMREQ (XLIB memory requirements) may be called to obtain conventional memory requirements for XLIB. Usage of XLIBMEMREQ is illustrated for Microsoft BASIC in Chapter 10. C and C++ do not allocate all conventional memory and therefore do not require any 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 (the default), 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 requires approximately 12K. One of the principle functions 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 4CH (DOS termination) issued either from real or protected mode. 7 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 = 10H or 11H, then a DOS error code is returned. If AX = DPMI error, then a DPMI 1.0 error code is returned (if provided by host). If AX = VCPI error, then DX = EAH = 0. If AX = XMS error, then an XMS error code is returned. Details: If both DPMI and VCPI are present and if EMS is enabled, 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. If EMS is disabled, then XLIB will always be configured for DPMI if it is present. This routine will possibly attempt to allocate conventional memory. The amount of memory XLIB will attempt to allocate can be obtained by calling XLIBMEMREQ (see below). The general address range from which this memory is allocated can be controlled by appropriately setting the high byte of IFLAGS. Alternative values for this byte are presented in Table 2. The byte has a value of 2 by default. This setting forces XLIB to allocate from the highest available address in conventional memory. Under certain settings of IFLAGS, XLIB can also allocate from upper memory (DOS 5.0 or higher). 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 4CH (DOS termination) issued from either real or protected mode. Table 2: IFLAGS Settings for Memory Allocation Control _____________________________________________________________________________ Bits 8-15 Implied Memory Allocation Location --------- ---------------------------------- 00H Lowest available address in conventional memory (00000H-9FFFFH) 01H Best fit in conventional memory 02H Highest available address in conventional memory 40H Lowest available address in upper memory (A0000H-FFFFFH) 41H Best fit in upper memory 42H Highest available address in upper memory 81H Follow strategy 40H by 00H if necessary 82H Follow strategy 41H by 01H if necessary 83H Follow strategy 42H by 02H if necessary _____________________________________________________________________________ 8 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 and if EMS is enabled, then XLIBMEMREQ will assume that DPMI will be used if bit 0 of IFLAGS is clear (the default); otherwise, VCPI is assumed. If EMS is disabled, then DPMI will always be assumed if present. 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. 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 set to zero 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 approximately equal to 12Kb. 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. 9 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; therefore, they can be useful for debugging. For example, an unidentified area of protected-mode code that is causing the system to crash can be isolated by placing jumps to EXITPM at alternative locations. 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. 10 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 3. 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 and interrupt flag, 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. 11 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 3: 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 and the interrupt flag, 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 4. 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 and the interrupt flag, 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). Most real-mode procedures 12 in XLIB issue calls to CALLPM; however, neither CALLPM nor ENTERPM are reentrant. For example, you cannot nest CALLPM/RETPM pairs. 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 4: 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 _____________________________________________________________________________ 13 5. Inline Mode Switching XLIB includes two routines to perform mode switching in C and C++ programs using inline assembly. These procedures are very versatile and simple. They switch the processor between real mode and 16-bit protected mode. A third routine is included to facilitate calls from 16-bit protected- mode to 32-bit near procedures in TSEG. Call INLINEPM to switch to 16-bit 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 therefore 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. Since the Microsoft C 7.0 compiler does not recognize 32-bit assembly instructions, the following program attains access to 32-bit registers by encoding prefixes in front of 16-bit instructions. These prefixes are inserted with the _emit directive. 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. A Borland C version of this program is contained in the file EXAMP2B.C. The Borland version is somewhat simpler than the following program because 32- bit registers can be used in Borland C provided that TASM is used to perform the assembly. This program may fail if an attempt is made to access logical addresses which are either protected or undefined in the page tables. 14 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*/ void main(void) { long l, xaddress; l = INITXLIB(); /*Initialize XLIB*/ if(l != 0) /*See if an error occurred*/ { printf("Library initialization error: %lX\n",l); return; } xaddress = 0x100000; /*Read first dword in 2ond meg*/ l = getextmem(xaddress); /*See if an error occurred*/ if(goterr != 0) { printf("Inline mode-switch error: %lX\n",l); return; } printf("[%lX] = %lX\n",xaddress,l); } long __far getextmem(long address) { __asm { som ;mov eax,[bp+6] mov ax,[bp+6] ; "" call INLINEPM ;Switch to 16-bit protected mode jc error ;Error code in ax mov ds,es:FLATDSEL ;Switch to flat data model sam ;push dword ptr [eax] som ; "" _emit 0ffh ; "" _emit 030h ; "" pop ax ;Return address contents in dx:ax pop dx call es:INLINERMPTR ;Switch back to real mode by calling jmp done ;INLINERM indirect. A direct call would ;load cs with an invalid value. error: xor dx,dx ;Return error code in dx:ax inc goterr ;Set error flag done: } } _____________________________________________________________________________ 15 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. System flags (including the interrupt flag) and control flags are preserved through the 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. 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 no changes to 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. System flags (including the interrupt flag) and control flags are preserved through the 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. 16 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. 17 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. 18 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 in 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 seldom uses 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 19 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 200H 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. 20 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. A real-mode interrupt handler may need to perform shifts to protected mode in order to gain access to extended memory. This mode switch should not be performed with CALLPM or ENTERPM if there is any possibility that the interrupt originated in protected mode or during a call through CALLRM. This follows because CALLPM and ENTERPM are not reentrant; consequently, calls to these procedures cannot be nested. The same may be said of INLINEPM and INLINERM. Use the SWITCHPM and SWITCHRM procedures discussed in Appendix G for this purpose. Real-mode hardware interrupt handlers which perform shifts to protected mode should be installed after the call to INITXLIB. Otherwise, the first interrupt may occur before protected-mode structures and code have been initialized. Interrupt handler should never call XLIB procedures apart from DEFLECTPM, SWITCHPM, and SWITCHRM. This limitation applies to handlers in either CPU mode. 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 21 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. XLIBE will in fact remap hardware interrupts in some situations (see Chapter 11). 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. 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. 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. 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. 22 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. 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 .9 is installed, then protected-mode interrupt vectors should be reset to original values before termination. Such action is not required for DPMI 1.0. 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. 23 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: Segment registers are preserved through the call. The interrupt flag is also preserved through the call. All other registers are returned at values existing as of the protected-mode IRET instruction. Details: The protected-mode handler will always receive interrupts disabled. It may enable interrupts if desired; however, once control is returned to DEFLECTPM, the interrupt flag will be reset to its original value. 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. 24 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 EMS (apart from calls to VCPI). Under DPMI and VCPI, the CPU will typically be operating in page mode. Under this mode of operation, memory may be remapped such that logical addresses are not equal to physical addresses. Addresses specified in the instruction code are logical addresses. The physical addresses are the locations in memory which are actually accessed. Typically, a programmer need not be concerned with the difference between these two types of addresses. However, the difference must be recognized when working with input/output devices which map to certain physical location in memory. XLIB includes routines called MAPIO and PMMAPIO which assign logical addresses to specified physical addresses. The physical addresses can then be accessed via the assigned logical addresses. 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). 25 Details: The block will always be paragraph aligned and will have size equal to an integer multiple of 16. The location in free memory from which the block is allocated will depend upon the allocation strategy in force as of call (see INT 21H function 58H). 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. 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. 26 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 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. 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. 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. CPU Mode: Protected Details: This routine is the protected-mode version of RESETMEM. See RESETMEM for specifications. 27 MAPIO (Map Input/Output Device) Purpose: Create a logical address mapping for an input/output device which maps to a fixed physical memory address. CPU Mode: Real Registers at Call: EDX = first physical address to be mapped. EAX = size of physical address block. Return Registers: EAX = 0 if successful, in which event, EDX = the logical address for accessing the physical memory. 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: DPMI will not allow mapping of physical addresses within the first megabyte; however, such mappings are allowed under other configurations. Logical addresses will always equal physical addresses in the absence of VCPI and DPMI because the processor will not be operating in page mode. In such cases, MAPIO simply performs validity checks on EDX and EAX. MAPIO will automatically allocate page tables as needed under VCPI. RESETMEM will release such tables. Memory-mapped input/output devices are not typically mapped within the range of available memory since this would leave the possibility of confusing such addresses with ordinary memory. Instead, such devices are mapped beyond the highest address that would otherwise be available. PMMAPIO (Protected Mode - Map Input/Output Device) Purpose: Create a logical address mapping for an input/output device which maps to a fixed physical memory address. CPU Mode: Protected Details: This routine is the protected-mode version of MAPIO. See MAPIO for specifications. 28 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 5. Table 5: 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. 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 four. 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) 29 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. 30 Details: 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. If this routine is called with BUFSIZE = 0, then XLIB will automatically set BUFADR and BUFSIZE to its own internal buffer. 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. 31 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. BLKADR/BLKSIZE may define a conventional memory block provided that this block is not overlapped by BUFADR/BUFSIZE. If this routine is called with BUFSIZE = 0, then XLIB will automatically set BUFADR and BUFSIZE to its own internal buffer. 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: Load 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. If CONDCODE = 0, then BLKSIZE = actual number of bytes transferred. 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. 32 PMXCREATE (Protected Mode - Create File) Purpose: Create and open a new file of specified name in specified directory. CPU Mode: Protected Details: This routine is the protected-mode version of XCREATE. See XCREATE for details. PMXOPEN (Protected Mode - Open File) Purpose: Open an existing file of specified name in specified directory. CPU Mode: Protected Details: This routine is the protected-mode version of XOPEN. See XOPEN for details. PMXCLOSE (Protected Mode - Close File) Purpose: Close previously opened file. CPU Mode: Protected Details: This routine is the protected-mode version of XCLOSE. See XCLOSE for details. PMXSAVE (Protected Mode - Save File) Purpose: Create file with contents equal to specified memory block. CPU Mode: Protected Details: This routine is the protected-mode version of XSAVE. See XSAVE for details. PMXLOAD (Protected Mode - Load File) Purpose: Load file contents to specified memory block. CPU Mode: Protected Details: This routine is the protected-mode version of XLOAD. See XLOAD for details. PMXWRITE (Protected Mode - Write to File) Purpose: Write specified memory block to specified location in open file. CPU Mode: Protected Details: This routine is the protected-mode version of XWRITE. See XWRITE for details. PMXREAD (Protected Mode - Read From File) Purpose: Load specified memory block from specified location in open file. CPU Mode: Protected Details: This routine is the protected-mode version of XREAD. See XREAD for details. 33 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. XLIB will also allow descriptors to be placed in the global descriptor table provided that DPMI is not being used. XLIB has no control over the global descriptor table under DPMI. The global descriptor table in XLIB begins at a public DWORD in DSEG called GDT. There are 48 descriptors in this table. The first 32 are either in use or are reserved by XLIB. Descriptors must be placed in the table by direct writes. The first available descriptor begins at DSEG offset OFFSET GDT + 100H. The selector values are simply the offsets from GDT. Hence, the selector value for the first available descriptor is 100H. 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 old 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. 34 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. An example for Borland C is included in the files EXAMP3B.C and EXAMP3B.ASM. 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 35 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 36 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) 37 '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 _____________________________________________________________________________ 38 11. Using XLIBE for Debugging and Exception Trapping XLIBE.LIB contains all of the functions of XLIB.LIB plus other functions designed for debugging and exception trapping. XLIBE.LIB consumes more memory than XLIB.LIB; consequently, the programmer may wish to use the former for program development and the latter for release versions. The same header and include files are used for both libraries. Typically, a VCPI server or a DPMI host will be accompanied with exception handlers. However, because such handlers do not have specific knowledge of the programs they will be serving, they typically do a relatively poor job of restoring machine state after an exception occurs. As a consequence, reboot is often necessary. Also, such handlers generally do a poor job of reporting the machine state as of the exception; consequently, they are of limited usefulness toward debugging programs. XLIBE.LIB contains protected-mode handlers for all CPU exceptions. These handlers will generally be able to perform complete cleanup after an exception occurs thereby eliminating the need for reboot. The state of the machine as of the exception is thoroughly reported; therefore, the programmer should be able to quickly identify the source of the exception. If DPMI 1.0 is being used, then XLIBE can also trap exceptions occurring in real mode. This ability makes XLIBE useful even to programmers who would otherwise be writing exclusively real-mode code. All of the additional features of XLIBE are activated by default. Once the call to INITXLIB has been successfully executed, protected-mode exception trapping will be enabled, and real-mode exception trapping will be enabled provided that DPMI 1.0 is installed. The default behavior of XLIBE can however be modified by IFLAGS, as described below. Two CPU exceptions are specifically designed for debugging. These are exception #1 and exception #3. Exception #1 is used for debug data breakpoints. This exception is generated with any attempt to access predefined addresses in either data or code. As many as four data breakpoint addresses may be defined with use of XLIBE procedures. Exception #3 is generated by the INT 3 instruction. This instruction may be inserted in the code at any point where the programmer wishes to inspect the processor state. The XLIBE handlers for both exceptions are designed to allow the programmer the option of resuming execution after the exceptions are reported. One of the notorious flaws in the IBM AT architecture is the manner in which hardware interrupts are programmed. Hardware interrupt requests (IRQs) 0-7 are assigned to interrupt vectors 8-15. Unfortunately, these same interrupt vectors are used for CPU exceptions. Consequently, when an interrupt occurs through one of these vectors, it is sometimes difficult to determine if the interrupt was generated by external hardware or by the CPU. For example, an INT 8 could be generated either by the system timer or by a double fault exception. The INT 8 handler should, if possible, make a determination of the source of the interrupt before servicing it. However, an easier and more reliable solution is to correct the problem at its root by remapping hardware interrupts to different vectors. The approach taken by XLIBE will depend upon the active protected-mode host/server. If DPMI is not installed, then the default behavior of XLIBE is to remap hardware interrupts during initialization. By default, XLIBE remaps IRQs 0-8 to vectors 50H-57H and IRQs 9-15 to vectors 58H-5FH. These vectors may however be changed by the programmer. The target vectors are contained in PIC1BASEINT (programmable interrupt controller #1 base interrupt) and 39 PIC2BASINT. Both variables are BYTE locations in DSEG. They should be changed before calling INITXLIB. The base interrupt vectors must be evenly divisible by eight. IRQs 9-15 are traditionally assigned to vectors 70H-77H. This mapping poses no problem because there is no conflict with CPU exceptions at these addresses. XLIB remaps to 50H-57H simply because these addresses have been used in other protected-mode software (e.g. DESQview and OS/2 1.x). XLIBE will not attempt to remap hardware interrupts if it discovers that the mappings have already been changed from their conventional locations. INITXLIB terminates with an error code in such cases. When XLIBE remaps hardware interrupts, it also installs real-mode handlers at the new hardware interrupt vectors. These handlers simply transfer execution to the addresses at the old vectors. This approach ensures complete compatibility with any resident software which may expect conventional hardware interrupt mappings. Accordingly, the programmer should install real-mode hardware interrupt handlers in usual fashion. For example, a timer-tick interrupt handler should be installed at INT 8. However, protected-mode handlers for these interrupts should be installed at the new vectors. The new vectors should be read from IRQ0INTNO and IRQ8INTNO after calling INITXLIB. The approach taken under DPMI is somewhat different. The DPMI host will expect conventional hardware interrupt mappings. The host will always immediately receive control after interrupts. The host will identify the interrupt as being a software interrupt, hardware interrupt, or CPU exception. It will then transfer control to the appropriate handler. Consequently, it is not necessary for XLIBE to remap hardware interrupts under DPMI. The values in PIC1BASEINT and PIC2BASEINT are ignored in this case. After the call to INITXLIB, IRQ0INTNO will equal 08H and IRQ8INTNO will equal 70H. XLIBE allows a high degree of control over exception trapping behavior. Three bits in IFLAGS are designed for this purpose. Bit 1 can be set to disable all exception trapping and remap of hardware interrupts. XLIBE is functionally equivalent to XLIB when this bit is set. Bit 2 may be set to disable real-mode exception trapping. This bit is meaningful only when DPMI 1.0 is installed and when bit 1 is clear. Bit 3 may be set to prevent XLIBE from accessing the CPU debug registers. This bit is meaningful only if bit 1 is clear. The programmer may wish to set this bit if a debugger is being used. All of these bits are clear by default. The programmer may selectively install exception handlers by altering the bit settings in CPUINTFLAGS (CPU interrupt flags). CPUINTFLAGS is a public DWORD in DSEG. A set bit enables installation of the the corresponding exception handler. The default value of CPUINTFLAGS is FFFBH. This enables installation of handlers for all exceptions apart from exception #2 (non- maskable interrupt), exception #16 (FPU exception), and exception #17 (alignment check). FPU exceptions are handled in the AT architecture through a hardware interrupt (IRQ 13) rather than exception #16. Alignment is seldom demanded in the AT architecture. When an exception occurs, the XLIBE exception handlers print useful information about the exception in a screen report. Appendix H contains instructions for interpreting this information and for identification of the source of the exception. 40 Detailed Specifications The procedures presented in this section are contained in XLIBE.LIB but not in XLIB.LIB. SETWATCH (Set Watchpoint) Purpose: Set a data breakpoint for generation of exception #1. CPU Mode: Real Registers at Call: EAX = linear address of breakpoint. DL = length of breakpoint in bytes. DH = type of breakpoint. DL must equal 1, 2, or 4. Alternative values for DH are: 0 - execution breakpoint, 1 - write breakpoint, and 2 - read/write breakpoint. 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). EBX = handle for releasing the breakpoint. Details: The linear address supplied in EAX must have alignment equal to the length supplied in DL. If an execute breakpoint is specified (DH = 0), then the length must equal 1 (DL = 1). Exception #1 is generated upon access of any byte within the breakpoint field. Up to four data breakpoints may be simultaneously defined. Under all protected-mode configurations except DPMI .9, the handle will equal the number of the assigned debug breakpoint register. The DPMI .9 specifications have no requirements for handle values. The exception report will print the value of DR6 (debug status register). This register may be examined to determine which breakpoint generated the exception. The lowest four bits contain this information. Set bits indicate that the corresponding breakpoint condition is satisfied. The presented value for DR6 is not relevant under DPMI .9. Under DPMI 1.0, the presented value is for a virtual DR6 and will not have exact correspondence with the actual DR6. In particular, bits 15 and 13 (BT and BD bits) are undefined. Under XLIB and VCPI mode switching, the actual DR6 is presented. If the programmer chooses to resume execution from the exception handler, then DR6 will be cleared before resumption. Breakpoints are globally enabled under XLIB and VCPI mode switching. Breakpoint visibility under DPMI will depend upon the host. Under XLIB or VCPI mode switching, exact breakpoint matching is used for 386 processors. This means that the reported CS:EIP points to the instruction which generated the exception rather than the instruction after. Such behavior is always the case with 486 and Pentium processors. Trapping behavior under DPMI will depend upon the host. FREEWATCH (Free Watchpoint) Purpose: Release previously allocated debug data breakpoint. CPU Mode: Real Registers at Call: EAX = watchpoint handle. 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). 41 RESETWATCH (Reset Watchpoints) Purpose: Release all previously allocated debug data breakpoints. CPU Mode: Real Registers at Call: None 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). Details: Either RESETWATCH or PMRESETWATCH will be called automatically by XLIBE upon program termination. PMSETWATCH (Protected Mode - Set Watchpoint) Purpose: Set a data breakpoint for generation of exception #1. CPU Mode: Protected Details: This routine is the protected-mode version of SETWATCH. See SETWATCH for details. PMFREEWATCH (Protected Mode - Free Watchpoint) Purpose: Release previously allocated debug data breakpoint. CPU Mode: Protected Details: This routine is the protected-mode version of FREEWATCH. See FREEWATCH for details. PMRESETWATCH (Protected Mode - Reset Watchpoints) Purpose: Release all previously allocated debug data breakpoints. CPU Mode: Protected Details: This routine is the protected-mode version of RESETWATCH. See RESETWATCH for details. 42 Appendix A: Description of XLIB Public Data The following is a summary of most public symbols for XLIB data. These symbols pertain to segment DSEG except where otherwise noted. This summary excludes many of the symbols presented in tables one through four. 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. Symbol: CPUINTFLAGS (CPU Interrupt Flags) Symbol Type: DWORD Default Setting: FFFBH Description: One can selectively enable installation of CPU exception handlers in XLIBE by appropriately setting the bits in CPUINTFLAGS. A set bit enables the corresponding exception. By default, installation of handlers for all exceptions is enabled except for nonmaskable interrupts, FPU exceptions, and alignment checks. FPU exceptions are handled through IRQ 13 rather than the CPU. Alignment checks are not typically made in the IBM AT architecture. The settings in CPUINTFLAGS have no effect on XLIB. Symbol: CSDSEGSEL (Code Segment Copy of DSEGSEL) Symbol Type: WORD Default Setting: Varies with operating environment Description: This is a WORD in TSEG containing the value of the DSEG selector. Since XLIB selector values are not constant, they must be read from memory locations. Such locations are placed in segment DSEG (see Table 1); however, one must know the DSEG selector to read these locations. Therefore, a copy of the DSEG selector is placed in the code segment where it can always be found. 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. 43 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. Symbol: GDT (Global Descriptor Table) Symbol Type: DWORD Default Setting: Not applicable Description: GDT is the first DWORD in the global descriptor table. This table contains 40 descriptors. The last sixteen are available for use. Descriptors must be placed in the table by direct writes. The first available descriptor begins at DSEG offset OFFSET GDT + 100H. The selector values are simply the offsets from GDT. Hence, the selector value for the first descriptor is 100H. The global descriptor table should not be used under DPMI. 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. 44 Symbol: IFLAGS (Initialization Flags) Symbol Type: WORD Default Setting: 0200 Description: IFLAGS is used by INITXLIB to control the initialization process. Bit 0 determines DPMI/VCPI priority in the event that both interfaces are present. If this bit is clear then DPMI will be installed; otherwise, VCPI will be installed. Bit 1 disables protected-mode exception trapping in XLIBE. It also disables remap of hardware interrupts. XLIBE is functionally equivalent to XLIB when this bit is set. The bit does not effect XLIB. Bit 2 disables real-mode exception trapping in XLIBE. This bit is meaningful only if DPMI 1.0 is installed and if bit 1 of IFLAGS is clear. The bit does not effect XLIB. Bit 3 disables XLIBE usage of debug registers. This bit is meaningful only if bit 1 of IFLAGS is clear. The bit does not effect XLIB. The upper byte of IFLAGS determines the strategy to be used by INITXLIB if conventional memory is to be allocated. The possible strategies are presented in Table 2. The default causes XLIB to allocate from the highest available address in conventional memory. 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 or by XLIBE. 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. 45 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: PIC1BASEINT/PIC2BASEINT (Programmable Interrupt Controller X Base Interrupt) Symbol Types: BYTE/BYTE Default Settings: 50H/58H Description: If exception trapping is enabled under XLIBE and if XLIB or VCPI mode switching is to be used, then hardware interrupts will be remapped during initialization. PIC1BASEINT specifies the starting interrupt number for the master programmable interrupt controller (IRQs 0-7). PIC2BASEINT specifies the starting interrupt number for the slave controller (IRQs 8-15). These variables should be set to the desired values before the call to INITXLIB. Both interrupt numbers must be evenly divisible by eight. These variables have no effect on XLIB. 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. 46 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. Error codes have been changed from earlier versions of XLIB. Condition Code Flags 01H FPU exception 02H Hot key pressed General Errors 10H Unable to identify operating environment 11H DOS memory allocation failure 12H DOS memory release error 13H Failed to enable A20 14H Insufficient logical address space 15H Insufficient number of handles 16H Bad handle 17H Bad selector 18H Bad address 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 20H Unable to remap hardware interrupts 21H Bad debug data watchpoint specification Errors Occurring Under DPMI (See Appendix C for codes returned by DPMI) 30H Protected mode initialization failure 31H Descriptor allocation error 32H Descriptor installation error 33H Unable to switch protected mode interrupt vector 34H Insufficient extended memory error 35H Extended memory allocation error 36H Extended memory release error 37H DOS memory allocation error 38H DOS memory release error 39H Unable to set descriptor base address 3AH Physical address mapping error 3BH Unable to get protected-mode exception vector 3CH Unable to set protected-mode exception vector 3DH Unable to get real-mode exception vector 3EH Unable to set real-mode exception vector 3FH Unable to set debug data breakpoint 40H Unable to release debug data breakpoint 47 Errors Occurring Under XMS (See Appendix D for codes returned by XMS) 50H Unable to enable A20 51H Unable to measure available extended memory 52H Insufficient extended memory error 53H Extended memory allocation error 54H Unable to lock extended memory 55H Unable to unlock extended memory 56H Extended memory release error Errors Occurring Under VCPI 60H Error in determining protected mode entry point 61H Unable to determine physical address of DOS memory page 62H Unable to determine hardware interrupt mappings 63H Insufficient extended memory error 64H Unable to determine number of free extended memory pages 65H Extended memory allocation error 66H Extended memory release error 67H Hardware interrupt remap error 48 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 49 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 50 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 in Example 3 will work here with no modification. C calls a real-mode 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 conventional 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. A Borland version of this program and of the accompanying library may be found in the files EXAMP3B.C and EXAMP3B.ASM. 51 _____________________________________________________________________________ /*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 /*Structure for passing arguments to SUMARRAY*/ { long condcode; long n; long address; float sum; } ad; main() { int i; long temp; float a[101]; temp = INITXLIB(); /*Initialize XLIB*/ if (temp != 0) /*See if an error occurred*/ { printf("Initialization Error: %lX\n",temp); return 0; } for(i = 0; i <= 100; i++) /*Initialize a[]*/ a[i] = i; ad.condcode = 0; /*Initialize the condition code*/ ad.n = 50; /*Will sum the first 50 elements in a[]*/ ad.address = LINADR(a); /*Compute and record linear address of a[]*/ SUMARRAY(&ad); /*Sum the array elements*/ if (ad.condcode != 0) /*See if an FPU error occurred*/ { printf("Error: %lX\n",ad.condcode); return 0; } printf("Sum: %f\n",ad.sum); /*The sum should be 1225*/ } _____________________________________________________________________________ 52 Appendix F: Technical Support Technical support for XLIB is provided to both registered and unregistered users; however, the registration fee is $60 per copy for those who have used technical support or who intend to use it. The additional charges will be waived for those who discover bugs in the XLIB libraries. Before contacting TechniLib with a problem, the following steps should be taken: 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 memory management software. 5) Try different options on your compiler, assembler, and linker. It is sometimes the case that code is not processed properly under some options. 6) If the problem results in a processor exception, then run the program with one of the exception trapping libraries (e.g. XLIBE.LIB) and make note of the report that is presented as of the exception. Technical support is available at: Dr. David Pyles TechniLib Company P.O. Box 6818 Jackson, Ms. 39282 (601) 372-7433 Problems may also be reported by CompuServe mail to CIS ID: 74730,167. 53 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 otherwise provided by XLIB. These routines do not conform to the general conventions followed by other XLIB procedures; consequently, they are presented in an appendix. In some cases the programmer may deem CALLPM and ENTERPM to be too slow for a particular mode switching task. Both procedures consume time in storing register state and in making other preparations for abrupt or unpredictable exits from protected mode (via jumps to RETPM/EXITPM or FPU exceptions). SWITCHPM and SWITCHRM perform mode switches in minimum CPU time. The greatest limitation to CALLPM/ENTERPM is that these procedures are not reentrant; consequently, calls to these procedures cannot be nested. This limitation could prove a problem if the programmer needed to perform a mode switch within a real-mode interrupt handler. If the interrupt originated in protected mode, then CALLPM/ENTERPM could not be used to perform the mode switch. Since interrupt handlers generally should not make assumptions about machine state, CALLPM/ENTERPM generally should not be used within such handlers. Use SWITCHPM and SWITCHRM instead. A less likely situation occurs where the programmer first enters protected mode via CALLPM/ENTERPM then executes real-mode code with CALLRM. The real-mode code could not then switch to protected mode with CALLPM/ENTERPM since this would be nesting the procedures. 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. SWITCHPM returns with CS = CSEGSEL and with all other segments equal to DSEGSEL. All other registers, including 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, including the status flags, are preserved. Each interrupt handler using SWITCHPM/SWITCHRM should have its own unique stack in DSEG in the event that nested interrupts occurred. After switching to 16-bit protected mode with SWITCHPM, one can transfer execution to a 32-bit segment with a far call. An example using the techniques discussed above is the MOVMEM function in the file EASYX.ASM. This function is not an interrupt handler; however, it is designed to be called from such handlers and must therefore abide by the same principles. 54 Appendix H: Debugging Unfortunately, neither Microsoft's CodeView nor Borland's Turbo Debugger are fully capable of debugging DOS-extended programs. The real-mode areas of such programs can be debugged with these debuggers provided that no attempts are made to step into calls to protected-mode subroutines. These subroutines must be debugged using other methods. This appendix presents useful information that should aid the programmer toward this end. Debugging protected-mode code without the aid of a software debugger will not prove as limiting as it may sound. In some cases there are better alternatives to software debuggers. For example, many programmers waste time stepping through code with a debugger trying to resolve a processor exception condition when a quick resolution to the problem could be gained with a deeper understanding of the processor and the exception. Resolving Bugs Affecting Program Output Bugs which lead to erroneous program output can typically be diagnosed with strategically located print statements. The XLIB archive contains a file call PMIO.INC which contains many protected-mode input/output routines, including a variety of print routines. The print routines never call BIOS or DOS (they use direct screen writes); consequently, they will work fine both in interrupt handlers and the main thread of execution. A similar method for locating bugs involves strategic location of the INT 3 instruction. This instruction will generate a breakpoint exception. The exception handlers in XLIBE will then give a complete report on register status as of the INT 3. The programmer will then be given the option to resume execution. Resolving Bugs Causing Processor Exceptions Bugs causing processor exceptions will commonly occur in protected mode. These bugs can be particularly annoying because they often hang or reboot the machine or leave it in an unstable state. However, the exception handlers in XLIBE should be able to successfully trap such bugs and then terminate the program in an orderly fashion. If the exception handlers in XLIBE fail to trap an exception, then it is likely the case that the bug is corrupting data or code used by the handlers. In any event, other methods may be used to track the bug. Once protected mode has been entered by calling either CALLPM or ENTERPM, one may return to the real-mode caller from any point in the code with a jump to either RETPM or EXITPM. Therefore, if an unidentified instruction is generating exceptions in protected mode, one may locate the instruction by placing jumps to RETPM or ENTERPM at various locations in the code. Initially the jumps should be placed early in the code and then moved forward. This way the processor exception is avoided as it is searched, thereby saving the time necessary to reboot after the exception occurs. 55 XLIB is also designed such that INT 21H, function 4CH should terminate a program from either real or protected mode. This provides an alternative way to exit the program before execution of a faulty instruction. Processor exceptions are not generally difficult to resolve if the programmer has a thorough understanding of the exception. When an exception occurs, the exception handlers in XLIBE will print a report of register state to the screen. The exception usually can be resolved by examining this report. In particular, the report will contain the CS:EIP at which the exception occurred. Using this information, the programmer can typically identify the problematic instruction by examining a list file generated by the assembler or compiler. Processor Exceptions and Their Causes Many processor exceptions issue error codes. The exception handlers in XLIBE will always display such codes. For all exceptions except page faults (exception #14), the error code will be nonzero if the processor can associate the exception with a particular segment, in which event, the error code identifies the segment. In particular, bits 3-15 of the error code will be the selector for the segment. Bit 1 will be set if this selector refers to a gate descriptor in the interrupt descriptor table. If this bit is clear, then bit 2 identifies the selector as belonging to the global descriptor table (the bit is clear) or the local descriptor table (the bit is set). Bit 0 is set if an event external to the program generated the exception. Error codes are generated for exceptions: 8,10,11,12,13,14,15, and 17. The following is a summary of nearly all CPU exceptions: Exception #0, Divide Error - This exception occurs when a DIV or IDIV has been executed with a zero divisor, or when a quotient is too large to be contained in the targeted register (AL, AX, or EAX). Exception #1, Debug Exception - This exception occurs when a data breakpoint has been addressed, or upon execution of any instruction when single-step trapping has been enabled by setting the trap flag in the EFLAGS register. If the exception is due to a data breakpoint, then the debug status register (DR6) identifies the breakpoint. The lowest four bits of this register correspond to the four CPU breakpoint registers (DR0-DR3). Set bits indicate that the corresponding breakpoint condition is satisfied. Bit 14 of DR6 will be set if the exception is due to a single-step trap. XLIB does not implement single-step trapping. Exception #3, Breakpoint - This exception is generated by INT 3. Exception #4, Overflow - This exception occurs when the INTO instruction is executed with the overflow flag being set. Exception #5, Bounds Check - This exception occurs when a BOUND instruction is executed with the operand violating the specified limits. Exception #6, Invalid Opcode - This exception occurs when an unrecognizable instruction is encountered in the code sequence. Provided that there are no bugs in the compiler, this exception is almost certainly indicative of a bad 56 transfer (JMP, CALL, RET, or IRET) or of code corruption. However, the exception can also be generated by instructions which are not intended for the current CPU mode (e.g. LLDT in real mode). Exception #7, Device Not Available - This exception occurs when an ESC instruction is executed under FPU emulation, or when a WAIT or ESC instruction is executed with the task-switch flag set in CR0. Neither case is likely under XLIB. Exception #8, Double Fault - If an exception occurs while the processor is attempting to call an exception handler for a prior exception, and if the two exceptions cannot be serialized, then a double fault exception is generated. The error code for this exception is always zero. Under XLIB, double faults are generally indicative of corruption of XLIB data used by the exception handlers. Exception #10, Invalid Task-State Segment - This exception occurs when an attempt is made to perform a task switch to a task having an invalid task- state segment. Since XLIB performs task switches only when servicing exceptions themselves, this condition generally derives from code corruption or an invalid transfer. Exception #11, Segment Not Present - This exception occurs when an attempt is made to load a segment register with a descriptor which is marked not-present. A segment might be so marked if its contents have been swapped to disk. Since all of the descriptors in XLIB are marked present, this exception likely derives from either corruption of the descriptor tables or an attempt to load a selector for an uninitialized descriptor. Exception #12, Stack Exception - Most stack exceptions are caused by limit violations in ESP or EBP. For example, if the value in ESP is beyond the stack segment limit then a PUSH or POP will generate a stack exception. It is also possible to have an "expand-down" stack where the segment limit is interpreted as a lower bound. In such cases a stack exception could be generated if a value in ESP or EBP were too small. Stack exceptions can also occur when SS is loaded with a selector for a descriptor which is marked not- present. Stack exceptions are generally common; however, they should be uncommon for XLIB programs while in protected mode. This follows because XLIB uses an expand-up stack with a FFFFFFFFH limit. Unless a switch has been made from the default stack, a stack exception under XLIB would typically be indicative of an invalid value being loaded to SS. Exception #13, General Protection Violation - This notorious exception is a catchall category on Intel processors that may be generated by a number of conditions. However, three conditions account for nearly all such exceptions in practice. These are: 1) An invalid value was loaded to a segment register, 2) An attempt was made to write to a code segment, or 3) Privilege rules were violated. Novices to protected mode often forget that segment registers must contain selectors in this mode. Invalid values are also frequently popped from the stack into segment registers because the stack has been corrupted or because the stack pointer has not been properly maintained. 57 It must be remembered that writes to code segments are illegal in protected mode. That is, one cannot write to a code segment through the CS register. If code modification is desired, then load a data segment register with a selector to a descriptor which maps the code segment. The CSEGDSEL and TSEGDSEL selectors are included in XLIB for this purpose. Protected-mode programs can run at four different privilege levels. The highest is level zero and the lowest is level three. XLIB programs will either be operating at the highest or lowest level. Real-mode code always operates at level zero. If a memory manager other than HIMEM.SYS is installed, then the machine is likely using virtual 8086 mode in lieu of real mode. This is also the case under Windows and OS/2. Virtual 8086 mode is a subset of protected mode wherein real mode is simulated. Virtual 8086 mode always operates at level three. XLIB programs operating under DPMI will nearly always be at level three. Under VCPI the privilege level will depend upon the CPU mode. In protected mode the privilege level is zero, but in virtual 8086 mode the privilege level is three. If neither DPMI nor VCPI are present, then XLIB programs will operate at level zero regardless of CPU mode. A program operating at level three is always under the supervision of another program at level zero. The supervisor program will typically be a memory manager, Windows, or OS/2. When the level three program attempts to execute a privileged instruction, the supervisor program is informed of the attempt by the processor. The supervisor program may then execute the instruction in behalf of the unprivileged program, or it may terminate the unprivileged program with a privileged operation violation. Privileged instructions include reads and writes to IO ports and alterations to the interrupt flag. Both of these types of instructions are generally permitted by the supervisor program. However, other instructions ( e.g. LGDT) will be denied. XLIB programmers have little occasion to use inadmissible privileged instructions. Therefore privileged operation exceptions are nearly always indicative of code corruption or of an invalid transfer. Exception #14, Page Fault - A principal protection mechanism of the processor is its memory paging mode. In this mode, memory is divided into 4K pages with the divisions being at 4K boundaries. A memory manager can mark certain pages as being absent, read-only, or privileged. An attempt to access an absent page or to write a read-only page always generates a page fault. A privileged page can be accessed only by code operating at privilege levels higher than three; hence, a page fault will occur if code at level three attempts to access such pages. When a page fault occurs, an error code is supplied by the processor to describe the fault. The error code is defined only in the lowest three bits. The zero bit describes whether the exception was caused by access of a not- present page (the bit is clear) or by a protection violation (the bit is set). Protection violations occur with attempts to access privileged pages or attempts to write read-only pages. Bit two of the error code describes the privilege mode at which the exception occurred. If the bit is clear, then the processor was executing in privileged mode. In this case, protection violations are generated only by attempts to write read-only pages. Bit one describes whether the fault took place during a read (the bit is clear) or a write (the bit is set). A page fault nearly always occurs because of an attempt to access a bad address or an attempt to transfer control to a bad address. Memory managers 58 may mark unallocated pages as being not-present so that page faults will be generated whenever an attempt is made to access them. This way the programmer will be alerted to bad address computations. When VCPI is being used, XLIB becomes largely responsible for management of memory pages. XLIB will in fact mark all unallocated pages as not present. XLIB will mark all allocated pages as unprivileged and writable. The CR2 register will contain the address for the attempted access which generated the page fault. CR2 is displayed by the exception handlers in XLIBE. Exception #13 and Exception #14 are by far the most common exceptions occurring under XLIB programming. The following list of questions should be considered when attempting to resolve such exceptions. These questions are also relevant to other exceptions: 1) Did you improperly maintain the stack pointer? 2) Did you forget that only selectors can be loaded to segment registers in protected mode? 3) Did you compute an address incorrectly? 4) Did you attempt to write to a code segment using CS override? 5) Did you fail to terminate a subroutine with RET? 6) Did you forget to properly match CALL (far or near) and RET (e.g. Did you forget that TSEG procedures must be near)? 7) Did you attempt to execute a privileged instruction? 8) Did you fail to terminate an interrupt handler with IRET? 9) Did you terminate a protected-mode interrupt handler with IRET instead of IRETD? 10) If you are a TASM programmer, did you fail to specify LARGESTACK in your TSEG code? Miscellaneous Debugging Tips If a program runs unpredictably even when executed under identical circumstances, then the problem is due to an asynchronous phenomenon. This almost definitely implies a problem with a hardware interrupt handler. Such problems are oftentimes associated with the timer tick interrupt. This theory can sometimes be tested by disabling certain hardware interrupts with the interrupt mask register. The interrupt mask register for IRQs 0 through 7 is at port 21H. The masks for IRQs 8 through 15 are at port A1H. Set bits disable the corresponding IRQ. For example, the following code sequence would disable the timer tick interrupt (IRQ 0): IN AL,21H OR AL,01H OUT 21H,AL If a program leaves the machine in an unstable state, then it is possible that XLIB is not being terminated correctly. Try running an unregistered copy of XLIB and see if the registration reminder message is printed to the screen at program termination. If not, then the XLIB termination handler is likely not receiving control. This is likely due to code corruption. 59 Most subroutines intended for protected-mode will also operate in real mode when applied to small problems. If a software debugger is deemed necessary, one might develop code in real mode and then convert to protected mode after debugging. 32-bit registers are admissible in real mode provided that no such register is used as a base or index register while containing a value greater than FFFFH. 60