The EMS TOOLKIT for C developers Copyright (c) 1989 Intel Corporation. All rights reserved. Intel Corporation, 5200 NE Elam Young Parkway, Hillsboro, OR 97124 First Edition December, 1989 DISCLAIMER Intel Corporation assumes no responsibility for errors that may appear in this manual. Nor does Intel make any commitment to update the information contained in this manual. Intel and Above are trademarks of Intel Corporation. LIMITED WARRANTY Intel Corporation excludes any and all implied warranties, including warranties of merchantability and fitness for a particular purpose. Intel makes no warranty of representation, either express or implied, with respect to this software, its quality, performance, merchantability, or fitness for a particular purpose. Intel shall not have any liability for special, incidental, or consequential damages arising out of or resulting from the use or modification of this software. This software is provided as is. USE OF PROGRAM PRODUCT You may use the Program Product on any one IBM or compatible personal computer, and copy the Object Code into any machine-readable form for your use of the Program Product; You may modify the Program Product material and/or merge or incorporate it into any general use software program of your development except for a utility program of similar nature to the Program Product. You may freely reproduce any such program of your development; however, the merged or incorporated part of the Program Product will continue to be subject to all other provisions of this agreement. CONTENTS CHAPTER 1. GETTING STARTED How to use this book 1-2 What's on the disks? 1-3 Which library should I use? 1-5 Requirements 1-7 Installing the software 1-7 CHAPTER 2. MEMLIB: LIBRARY FOR C DEVELOPERS Compiling and linking MEMLIB 2-3 Suggested sequence for using MEMLIB functions 2-4 MEMLIB functions list 2-5 -- 2-31 CHAPTER 3. EMMLIB: LIBRARY FOR ASSEMBLY LANGUAGE AND C DEVELOPERS EMMLIB functions grouped by operation 3-3 EMMLIB functions in alphabetical order 3-7 APPENDIX A. EXAMPLE PROGRAM APPENDIX B. ERROR MESSAGES APPENDIX C. TECHNICAL INFORMATION ABOUT MEMLIB How MEMLIB provides access to expanded memory C-1 How MEMLIB keeps track of expanded memory C-2 How MEMLIB allocates expanded memory C-2 Algorithms C-4 Chapter 1 GETTING STARTED This manual describes the toolkit for the Lotus-Intel-Microsoft Expanded Memory Specification (EMS). The toolkit offers experienced Microsoft C developers a quick and easy way to use expanded memory in their application programs. If you are writing programs in some other Microsoft language (Fortran, Pascal, etc.), you may still be able to use the toolkit. Read the mixed-language programming section in the Microsoft manual to find out how tomake calls to C from your language. The toolkit includes the following libraries: MEMLIB A set of C functions that perform the "housekeeping" necessary to access and store data in expanded memory. When you use MEMLIB, you won't need to worry about page frame size, 16K-byte boundaries, or interfacing with an assembly language device driver. See Chapter 2. EMMLIB A set of C-callable assembly language functions that call the EMS driver directly. See Chapter 3. These libraries allow you to use the full functionality of EMS without spending countless hours writing assembly language programs. This manual supplements the Lotus-Intel-Microsoft Expanded Memory Specification manual (also referred to as the EMS manual). The LIM specification defines the software interface between an application program and the expanded memory used by the program. How to use this book -------------------- This manual explains the features and operation of the EMS Toolkit libraries. Once you are familiar with the libraries and how to use them, you can use this book as a quick reference for function names, parameters, and error messages. Here's what you'll find in each chapter of the manual: Chapter 1 -- Getting Started: This is the chapter you're reading now. Besides introducing you to the EMS Toolkit, this chapter lists the contents of the Intel diskettes, offers suggestions for which library to use, and tells you how to install the software. Chapter 2 -- MEMLIB: Library for C Developers This chapter describes how the MEMLIB library works, how to write code to use MEMLIB, and how to compile and link MEMLIB. The last section (which makes up the bulk of this chapter) describes each of the MEMLIB functions in detail. Chapter 3 -- EMMLIB: Library for Assembly Language and C Developers Chapter 3 describes the EMMLIB library and how it works, and describes how to compile and link EMMLIB. This chapter also includes lists of EMMLIB functions, and tells you how to get more information about each one. Appendix A -- Example Progra: This short example program demonstrates how to use MEMLIB to store and access data in expanded memory. Appendix B -- Error Messages: This appendix lists the error messages you may incur from MEMLIB, and suggests corrective actions. Appendix C -- Technical Information About MEMLIB: This appendix explains the more technical aspects of how MEMLIB allocates and frees expanded memory. You don't need to know this information to use MEMLIB. What's on the disks? -------------------- The EMS Toolkit package includes four diskettes. You'll need disks 1, 2 and 3 to use either of the toolkit libraries. If you are an assembly language developer and want to use expanded memory to execute code, you may want to use disk 4. Disk 4 contains examples for assembly language programmers only. (You don't need disk 4 to use MEMLIB or EMMLIB.) The Intel EMS Toolkit diskettes contain the following files: Disk 1 Filename Description MEMLIB.C MEMLIB source code (functions) MEMLIB.H MEMLIB include file (function prototypes) MEMINTRL.C MEMLIB source code (internal MEMLIB utilities) ERRORS.H MEMLIB error messages EMMLIB.LIB EMM large-model library of C interface routines EMMLIB.H EMMLIB include file Disk 2 Filename Description SAMPLE.C Short sample program using MEMLIB functions SAMPLE.MAK "make" file for use with SAMPLE.C program ROLODEX.C More detailed sample program using MEMLIB and EMMLIB ROLODEX.H Header file for use with ROLODEX.C ROLODEX.DAT Data file for ROLODEX.C ROLODEX.MAK "make" file for use with ROLODEX.C program EMMLIB Directory containing EMMLIB function files Disk 3 Filename Description EMMLIB Directory containing the rest of the EMMLIB function files and other EMMLIB support files Disk 4 Filename Description LOADCODE.ASM Sample assembly language program that demonstrates how to execute code from expanded memory HELLO.ASM Sample code the LOADCODE.ASM will execute LOADCODE.MAK "make" file for LOADCODE.ASM Which library should I use? --------------------------- The way in which your application will use expanded memory determines which library you should use. o If you are writing code in C and you want to store data in expanded memory, you can use MEMLIB; it offers routines to do this quickly and easily. o If you understand the Lotus-Intel-Microsoft Expanded Memory Specification and want to make expanded memory calls directly from C, you must use EMMLIB. For example, if you want to use named handles for the blocks of expanded memory you allocate, you need to use EMMLIB. o If you want to use expanded memory for more complex uses such as executing code, you must write your program in assembly language and make EMS function calls directly. Requirements ------------ The functions in the EMS Toolkit libraries assume a few things about your computer's environment. + o Both MEMLIB and EMMLIB assume that Microsoft C version 5.1 and its "include" files are available on your system. o MEMLIB assumes that you are using a C "large-memory-model." EMMLIB allows you to use any size memory model. o The sample "make" files assume that the Microsoft utility MAKE.EXE is available; that the source code, header files, and EMMLIB.LIB are in one directory; and that the EMMLIB function files are in a subdirectory named \EMMLIB. Intel recommends that you install the MEMLIB files and your application's source code in the same subdirectory. Installing the software ----------------------- The following sequence is the way we suggest you install the EMS Toolkit software. For this example, suppose the application source code is kept a subdirectory named \APP. 1 Insert Intel Toolkit diskette 1 into drive A. 2 At the DOS prompt, type the following: XCOPY A:\*.* C:\APP /s 3 Repeat the first two steps with diskettes 2 and 3. This command will copy the contents of the Intel disks to the \APP directory on drive C. /s means that all files in subdirectories below A:\ will be copied, and will keep the same directory structure. (If you'd like more information about the XCOPY command, refer to a DOS manual.) Chapter 2 MEMLIB: LIBRARY FOR C DEVELOPERS MEMLIB is a set of functions that allow developers to manage expanded memory similar to the way they manage conventional memory with C. MEMLIB functions look and act like some of the more familiar C functions (malloc and free, for example). MEMLIB's internal structure does the "housekeeping" necessary to access and store data in expanded memory. When you use MEMLIB, you won't need to worry about page frame size, 16K-byte boundaries, or interfacing with an assembly language device driver. MEMLIB lets you allocate and access any size block of memory up to 64K bytes. It tracks available free memory, and uses a "best fit" algorithm to allocate new blocks. When you use MEMLIB, you just make a few C calls, and the libraries do the rest. Your application makes a call to MEMLIB, and MEMLIB in turn calls the EMMLIB library to access expanded memory. EMMLIB then translates the C calls into assembly language, and passes the calls on to the EMS driver. Compiling and linking MEMLIB ---------------------------- Because MEMLIB uses EMMLIB calls to perform its functions, you must link and compile both libraries when you use MEMLIB. (The toolkit includes a previously made EMMLIB.LIB file to use with MEMLIB.) You can compile and link either with "make" (a Microsoft C utility) or manually. Using the "make" file --------------------- The easiest way to compile and link a number of interdependent modules is to use a "make" file. A "make" file lists all of the modules and their dependencies in a single file. A "make" file for a program called "SAMPLE" to use MEMLIB should contain the following lines. (contents of the file sample.mak) memlib.obj: memlib.c memlib.h errors.h emmlib.h CL /C /AL memlib.c sample.obj: sample.c memlib.h errors.h CL /C /AL sample.c sample.exe: sample.obj memlib.obj link sample memlib,,, emmlib.lib To use the "make" file, type the following line at the DOS prompt: make sample.mak If you change any file, just run the "make" file again to recompile and relink. Compile and link manually ------------------------- Use the following command to compile the MEMLIB library: CL /C /AL memlib.c Use the following command to link MEMLIB to a program called "SAMPLE": LINK sample memlib, sample, nul, emmlib.lib Suggested sequence for using MEMLIB functions --------------------------------------------- MEMLIB functions provide a convenient way for your application to store data in expanded memory. To use expanded memory (and MEMLIB) most effectively, your application should follow this sequence of actions: 1. Allocate memory efmalloc allocates a block of expanded memory and returns a token (a block ID) to the application. 2. Access memory seteptrs uses the token returned by efmalloc to access the blocks you allocated. Also set1eptr, set2eptrs, set3eptrs. 3. Free memory effree frees previously allocated blocks of memory. Also effreeall. The other MEMLIB functions are provided for more specialized uses. You don't need to use them to store data in expanded memory. **CAUTION** Be sure to check the error codes that MEMLIB returns. If you try to allocate and use expanded memory when EMM isn't installed, you may lose data or overwrite some other area of memory. To learn more about MEMLIB's error codes, read Appendix B. The next section describes all of the MEMLIB functions in detail. Each description includes the purpose of the function, the calling sequence to use, and a short example program. The functions are listed in alphabetical order for easy reference. Below is a complete list of the functions grouped according to the operations they perform. To learn about a particular function, turn to the specified page. Function Page Allocating Memory efmalloc 2-10 Accessing Memory seteptrs 2-24 set1eptr 2-28 set2eptrs 2-30 set3eptrs 2-32 Freeing Memory effree 2-6 effreeall 2-8 Other ememavl 2-12 ememmax 2-14 emsize 2-16 push_context 2-21 pop_context 2-18 This is all you need to begin using MEMLIB to get expanded memory for your application program. If you would like more detailed information about how MEMLIB works, read Appendix C, Technical Information about MEMLIB. EFFREE ------ PURPOSE The effree function frees a block of memory that efmalloc had allocated. Effree is analogous to the standard C function free. **CAUTION** The expanded memory manager (MEMLIB) is NOT a part of DOS. MEMLIB will not automatically free your allocated blocks when you exit your application. Your application should free all blocks during any kind of exit, including or other error conditions. (See Function 6, effreeall.) CALLING SEQUENCE unsigned int status; status = effree (token); unsigned int token The token that efmalloc returned when it allocated that block of memory. (input) STATUS PASSED (zero) The block is now free. error (non-zero value) See Appendix B for descriptions of error codes. EXAMPLE #include "memlib.h" #include "errors.h" unsigned int token; unsigned int status; status = efmalloc (100, &token); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = effree (token); if (status == PASSED) /* Token is free, continue normal code */ else /* error condition */ EFFREEALL --------- PURPOSE The effreeall function frees all expanded memory blocks, pages, and handles effreeallallocated by your application. Effreeall is a convenient function to use when you are ending your application. **CAUTION** The expanded memory manager (MEMLIB) is NOT a part of DOS. MEMLIB will not automatically free your allocated blocks when you exit your application. Your application should free all blocks during any kind of exit, including or other error conditions. CALLING SEQUENCE unsigned int status; status = effreeall ( ); STATUS PASSED (zero) All blocks, handles, and pages are now free. error (non-zero value) See Appendix B for descriptions of error codes. EXAMPLE #include "memlib.h" #include "errors.h" unsigned int status; ... status = effreeall(); if (status == PASSED) printf ("effreeall() successful"); else /* error condition */ EFMALLOC -------- PURPOSE The efmalloc function is analogous to the standard C function malloc. In the same way that malloc allocates conventional memory, the efmalloc function allocates a block of memory from expanded memory pool. You can allocate up to 64K bytes with a call to efmalloc. There is one significant difference between malloc and efmalloc. A call to malloc allocates memory and returns a pointer, giving you access to that memory. Efmalloc allocates memory, but doesn't return a pointer. Here's why: You can't access expanded memory the same way you access conventional memory. You have to map a block of expanded memory into the page frame before you can access it. Once you've mapped in a block, you get a pointer to that block. With MEMLIB, you access a block using the seteptrs function (also set1eptr, set2eptrs, and set3eptrs). See the seteptrs function for more information about accessing expanded memory. **NOTE** You must use the effree (or effreeall) function to free a block of memory allocated with efmalloc. CALLING SEQUENCE unsigned int status; status = efmalloc (size, &token); unsigned int size The size (in bytes) desired for the memory block. The size must be greater than zero. (input) unsigned int token An identifier assigned to the memory block you've allocated. The token identifies that memory block in subsequent calls to other functions such as set1eptr. (output) STATUS PASSED (zero) The memory manager has allocated the block as expanded memory. error (non-zero value) See Appendix B for descriptions of error codes. EXAMPLE #include "memlib.h" #include "errors.h" unsigned int size; unsigned int status; unsigned int token; size = 100 * sizeof(int); status = efmalloc (size, &token); if (status == PASSED) /* Continue normal code. We have allocated */ /* space to store 100 integer values. */ else /* error condition */ EMEMAVL ------- PURPOSE The ememavl function computes the amount (in bytes) of expanded memory available in your computer. The amount returned by ememavl is the total of all expanded memory available -- it is not the largest contiguous block of expanded memory (see ememmax). CALLING SEQUENCE unsigned int status; status = ememavl (&size) unsigned long size The amount of expanded memory (in bytes) ememavl returns (output) STATUS PASSED (zero) The memory manager has returned the total amount of expanded memory. error (non-zero value) See Appendix B for descriptions of the error codes. EXAMPLE #include "memlib.h" #include "errors.h" unsigned int status; unsigned long size; status = ememavl (&size); if (status == PASSED) printf ("Exp. memory avail. = %lu bytes \n", size); else /* error condition */ EMEMMAX ------- PURPOSE The ememmax function returns the size (in bytes) of the largest usable ememmaxblock of expanded memory. Because blocks of free memory can be fragmented, this amount may be smaller than the total amount of free expanded memory. CALLING SEQUENCE unsigned int status; status = ememmax (&size); unsigned int size The number of bytes in the largest usable block of expanded memory. (output) STATUS PASSED (zero) The memory manager has returned the size of the largest usable block. error (non-zero value) See Appendix B for descriptions of the error codes. EXAMPLE #include "memlib.h" #include "errors.h" unsigned int status; unsigned int size; status = ememmax (&size); if (status == PASSED) printf ("Largest block available = %u bytes \n", size); else /* error condition */ EMSIZE ------ PURPOSE The emsize function computes the size of a previously allocated block of expanded memory. Identify the block using the token that efmalloc returned emsizeafter allocating it. CALLING SEQUENCE unsigned int status; status = emsize (token, &size); unsigned int token Identifier for the block. (input) unsigned int size The size of the block (in bytes). (output) STATUS PASSED (zero) The token was valid; the size is computed. error (non-zero value) See Appendix B for descriptions of error codes. EXAMPLE #include "memlib.h" #include "errors.h" unsigned int status; unsigned int token; unsigned int size; status = efmalloc (200, &token); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = emsize (token, &size) if (status == PASSED) printf ("Size of block = %u bytes \n", size); else /* error condition */ POP_CONTEXT ----------- PURPOSE The pop_context function restores the context saved by the push_context function. **NOTE** When you call pop_context, you lose access to the blocks that were mapped in at the time of the call. CALLING SEQUENCE unsigned int status; status = pop_context( ); STATUS PASSED (zero) The context of block ( ) is restored. error (non-zero value) See Appendix B for descriptions of error codes. EXAMPLE #include "memlib.h" #include "errors.h" unsigned int status; unsigned int token1; unsigned int token2; void *pointer1; void *pointer2; status = efmalloc (100, &token1); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = efmalloc (200, &token2); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = set1eptr (token1, &pointer1); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = push_context(); if (status != PASSED) /* error condition */ else /* save context for block 1 */ status = set1eptr (token2, &pointer2); if (status != PASSED) /* error condition */ else /* context for block 2 is active -- */ /* can't access block 1 */ ... status = pop_context(); if (status != PASSED) /* error condition */ else /* context for block 1 is active -- */ /* can't access block 2 */ PUSH_CONTEXT ------------ PURPOSE The push_context function provides a convenient way to make the context of a mapped block available for later use. The context tells MEMLIB which blocks are mapped into which physical pages. Push_context saves the context, making it easy to restore access to whatever block(s) were mapped in at the time of the call to push_context. Restore the context you've "pushed" using the pop_context function (see Function 12). CALLING SEQUENCE unsigned int status; status = push_context( ); STATUS PASSED (zero) The memory manager has pushed the context of the block onto a stack. error (non-zero value) See Appendix B for descriptions of error codes. **NOTE** To save a context, MEMLIB needs to allocate a small amount of conventional memory. MEMLIB allocates this memory dynamically-- allocating just enough memory to save the context at the time of the call (usually less than 20 bytes). EXAMPLE #include "memlib.h" #include "errors.h" unsigned int status; unsigned int token1; unsigned int token2; void *pointer1; void *pointer2; status = efmalloc (100, &token1); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = efmalloc (200, &token2); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = set1eptr (token1, &pointer1); ... if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = push_context(); if (status != PASSED) /* error condition */ else /* save context for block 1 */ ... status = set1eptr (token2, &pointer2); if (status != PASSED) /* error condition */ else /* context for block 2 is active -- */ /* can't access block 1 */ ... status = pop_context(); if (status != PASSED) /* error condition */ else /* context for block 1 is active -- */ /* can't access block 2 */ SETEPTRS -------- PURPOSE The seteptrs function attempts to gain access to the blocks you specify. Seteptrs then returns pointers to those blocks. Use this function if you need access to more than three blocks at once. Use set1eptr, set2eptrs, or set3eptrs to access 1, 2, or 3 blocks of memory. You can access all allocated blocks, but not necessarily all at the same time. You can access only that memory which is mapped into the page frame. Each expanded memory page is 16K, so if your page frame is 64K bytes, it will hold four pages, and any four blocks of 16K or less can be mapped in at one time. (EMS page frames are guaranteed to be at least 64K bytes.) If the combined size of all memory blocks you allocate during your program's execution is less than the size of your page frame, you can access all of the blocks at once. For a more detailed description of page frames, read Chapter 1 in the EMS manual. **NOTE** Seteptrs will unmap any pages that were mapped in before the call to seteptrs. If you want to call seteptrs, but want to regain access to a block that had been mapped in before the call, use the push_context function (before you call seteptrs). See push_context. CALLING SEQUENCE unsigned int status; status = seteptrs (num_blocks, tokens, pointers); unsigned int num_blocks The number of blocks you want to access. (input) unsigned int tokens[num_blocks] An array of tokens that efmalloc returned when it allocated the memory blocks. (input) void *pointers[num_blocks] The array of pointers seteptrs returns. Pointer[i] will point to the block of memory identified by token[i]. (output) STATUS PASSED (zero) You can gain access to all of the specified blocks. error (non-zero value) See Appendix B for descriptions of error codes. EXAMPLE #include "memlib.h" #include "errors.h" unsigned int tokens[2]; /* Set array size for the number of blocks */ /* you wish to allocate (in this case, 2) */ void far *pointers[2]; unsigned int status; status = efmalloc (200, &token[0]); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = efmalloc (600, &token[1]); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = seteptrs (2, tokens, pointers) if (status == PASSED) /* blocks are mapped in -- use */ /* pointers to reference them */ else /* error condition */ SET1EPTR -------- PURPOSE The set1eptr function provides access to a single block of memory by calling seteptrs. (This is a convenience routine for programmers who don't want to declare the arrays required by seteptrs.) CALLING SEQUENCE unsigned int status; status = set1eptr (token, &pointer); unsigned int token The token that efmalloc returned when it allocated that block of memory. (input) void *pointer The pointer that set1eptr returns to the application. (output) STATUS PASSED (zero) You have access to the block. error (non-zero value) See Appendix B for descriptions of error codes. EXAMPLE #include "memlib.h" #include "errors.h" void *pointer; unsigned int token; unsigned int status; status = efmalloc (500, &token); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = set1eptr (token, &pointer); if (status == PASSED) /* You have access to that block. Use */ /* the pointer as the block's reference. */ else /* error condition */ SET2EPTRS --------- PURPOSE The set2eptrs function provides access to two blocks of expanded memory by calling seteptrs. (This is a convenience routine for programmers who don't want to declare the arrays required by seteptrs.) CALLING SEQUENCE unsigned int status; status = set2eptrs (token1, token2, &pointer1, &pointer2); unsigned int token1 The token efmalloc returned when it allocated the first block of memory. (input) unsigned int token2 The token efmalloc returned when it allocated the second block of memory. (input) void *pointer1 The pointer to the first block that set2eptrs returns. (output) void *pointer2 The pointer to the second block that set2eptrs returns. (output) STATUS PASSED (zero) You have access to these two blocks. error (non-zero value) See Appendix B for descriptions of error codes. EXAMPLE #include "memlib.h" #include "errors.h" void *pointer1; void *pointer2; unsigned int token1; unsigned int token2; unsigned int status; status = efmalloc (500, &token1); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = efmalloc (900, &token2); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = set2eptrs (token1, token2, &pointer1, &pointer2); if (status == PASSED) /* You have access to these blocks. */ /* Use the pointers as references. */ else /* error condition */ SET3EPTRS --------- PURPOSE The set3eptrs function provides access to three blocks of expanded memory by calling seteptrs. (This is a convenience routine for programmers who don't want to declare the arrays required by seteptrs.) CALLING SEQUENCE unsigned int status; status = set3eptrs (token1, token2, token3, &pointer1, &pointer2, &pointer3); unsigned int token1 The token efmalloc returned when it allocated the first block of memory. (input) unsigned int token2 The token efmalloc returned for the second block. (input) unsigned int token3 The token efmalloc returned for the third block. (input) void *pointer1 The pointer to the first block that set3eptrs returns. (output) void *pointer2 The pointer to the second block that set3eptrs returns. (output) void *pointer3 The pointer to the third block. (output) STATUS PASSED (zero) You can gain access to these three blocks. error (non-zero value) See Appendix B for descriptions of error codes. EXAMPLE #include "memlib.h" #include "errors.h" void *pointer1; void *pointer2; void *pointer3; unsigned int token1; unsigned int token2; unsigned int token3; unsigned int status; status = efmalloc (500, &tok1); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = efmalloc (900, &tok2); if (status != PASSED) /* error condition */ else /* continue normal code */ ... set3eptrs status = efmalloc (300, &tok3); if (status != PASSED) /* error condition */ else /* continue normal code */ ... status = set3eptrs (token1, token2, token3, &pointer1, &pointer2, &pointer3); if (status == PASSED) /* Blocks are allocated; use pointers as references */ else /* error condition */ Chapter 3 EMMLIB: LIBRARY FOR ASSEMBLY LANGUAGE AND C DEVELOPERS EMMLIB is a collection of assembly language functions that call EMS functions directly. This library provides full EMS capability to C programmers; EMMLIB performs all of the functions listed in the Lotus-Intel-Microsoft Expanded Memory Specification. If you want to use expanded memory for more complex uses such as named handles, alternate register sets, or page aliasing, then you will need the advanced functions available from EMMLIB. **NOTE** LIM-EMS is a powerful and complex specification. The EMMLIB routines are designed to allow high level languages to use the full power of EMM directly; the routines are not designed to simplify the specification. The LIM specification has approximately 30 functions, and most of these functions have subfunctions. EMMLIB provides C-callable routines for all of the functions and subfunctions (a total of 59). The EMMLIB functions are described in comment headers in the .ASM files on the Intel diskettes. Each filename includes the number of the EMS function corresponding to the EMMLIB function. Table 3-1 is a list of the functions available in EMMLIB, grouped according to the operations they perform. The table includes the name of the function, the file containing the code and description, and the corresponding function number from EMS. Table 3-2 lists the functions in alphabetical order, and includes cross references to the filename and corresponding EMS function. For a detailed description of a function, read the file using any text editor, or read the description of the corresponding assembly language function in the EMS manual. For example, to map in a couple of pages of expanded memory, you could use the map_unmap_pages function. To learn how to use this function, you would read either file EMMLIB23.ASM or the description of Function 17 in the EMS manual. **NOTE** EMMLIB.H is a header file that provides your C code with all of the "typedefs", function prototypes, and "#defines" that you need to call the EMMLIB functions. Be sure to include this file when you use any EMMLIB function. Table 3-1 EMMLIB Functions grouped by operation ______________________________________________________________ Function Name Filename EMS Function No. _____________________________________________________________ presence, status, and version EMM_installed EMM01-B.ASM 1 get_EMM_status EMM01-A.ASM 1 get_EMM_version EMM07-A.ASM 7 memory allocation get_unalloc_page_count EMM03-A.ASM 3 get_alloc_page_count EMM03-B.ASM 3 get_total_page_count EMM03-C.ASM 3 alloc_pages EMM04-A.ASM 4 dealloc_pages EMM06-A.ASM 6 realloc_pages EMM18-A.ASM 18 get_unalloc_raw_page_count EMM26-A.ASM 26 get_alloc_raw_page_count EMM26-B.ASM 26 get_total_raw_page_count EMM26-C.ASM 26 alloc_std_pages EMM27-A.ASM 27 alloc_raw_pages EMM27-B.ASM 27 mappable memory region information get_page_frame_seg EMM02-A.ASM 2 get_mappable_conv_regions EMM25-A.ASM 25 get_mappable_exp_regions EMM25-A.ASM 25 get_mappable_regions EMM25-A.ASM 25 get_mappable_conv_region_count EMM25-A.ASM 25 get_mappable_exp_region_count EMM25-A.ASM 25 get_page_frame_count EMM25-A.ASM 25 get_mappable_region_count EMM25-C.ASM 25 memory mapping map_unmap_page EMM05-A.ASM 5 map_unmap_pages EMM17-A.ASM 17 memory mapping context save_context EMM08-A.ASM 8 restore_context EMM09-A.ASM 9 get_context EMM15-A.ASM 15 set_context EMM15-B.ASM 15 get_set_context EMM15-C.ASM 15 get_context_size EMM15-D.ASM 15 get_partial_context EMM16-A.ASM 16 get_partial_context_size EMM16-C.ASM 16 set_partial_context EMM16-B.ASM 16 memory movement and exchange move_memory_region EMM24-A.ASM 24 xchg_memory_region EMM24-B.ASM 24 handle management get_handle_count EMM12-A.ASM 12 get_handle_pages EMM13-A.ASM 13 get_all_handles_pages EMM14-A.ASM 14 get_handle_attrib EMM19-A.ASM 19 set_handle_attrib EMM19-B.ASM 19 get_attrib_capability EMM19-C.ASM 19 get_handle_name EMM20-A.ASM 20 set_handle_name EMM20-B.ASM 20 get_handle_dir EMM21-A.ASM 21 search_handle_name EMM21-B.ASM 21 get_total_handles EMM21-C.ASM 21 program flow control alter_map_jump EMM22-A.ASM 22 alter_map_call EMM23-A.ASM 23 get_alter_map_call_stack_size EMM23-B.ASM 23 Operating System only enable_OS_fcns EMM30-A.ASM 30 disable_OS_fcns EMM30-B.ASM 30 return_OS_access_key EMM30-C.ASM 30 get_hw_info EMM26-D.ASM 26 get_alt_reg_set EMM28-A.ASM 28 set_alt_reg_set EMM28-B.ASM 28 get_alt_context_size EMM28-C.ASM 28 alloc_alt_reg_set EMM28-D.ASM 28 dealloc_alt_reg_set EMM28-E.ASM 28 alloc_DMA_reg_set EMM28-F.ASM 28 enable_DMA_reg_set EMM28-G.ASM 28 disable_DMA_reg_set EMM28-H.ASM 28 dealloc_DMA_reg_set EMM28-I.ASM 28 prep_EMM_warmboot EMM29-A.ASM 29 _____________________________________________ Table 3-2 EMMLIB Functions in alphabetical order ________________________________________________________________ Function Name Filename EMS Function No. _________________________________________________________________ alloc_alt_reg_set EMM28-D.ASM 28 alloc_DMA_reg_set EMM28-F.ASM 28 alloc_pages EMM04-A.ASM 4 alloc_std_pages EMM27-A.ASM 27 alloc_raw_pages EMM27-B.ASM 27 alter_map_jump EMM22-A.ASM 22 alter_map_call EMM23-A.ASM 23 dealloc_alt_reg_set EMM28-E.ASM 28 dealloc_DMA_reg_set EMM28-I.ASM 28 dealloc_pages EMM06-A.ASM 6 disable_DMA_reg_set EMM28-H.ASM 28 disable_OS_fcns EMM30-B.ASM 30 EMM_installed EMM01-B.ASM 1 enable_DMA_reg_set EMM28-G.ASM 28 enable_OS_fcns EMM30-A.ASM 30 get_all_handles_pages EMM14-A.ASM 14 get_alloc_page_count EMM03-B.ASM 3 get_alloc_raw_page_count EMM26-B.ASM 26 get_alt_context_size EMM28-C.ASM 28 get_alter_map_call_stack_size EMM23-B.ASM 23 get_alt_reg_set EMM28-A.ASM 28 get_attrib_capability EMM19-C.ASM 19 get_context EMM15-A.ASM 15 get_context_size EMM15-D.ASM 15 get_EMM_status EMM01-A.ASM 1 get_EMM_version EMM07-A.ASM 7 get_handle_count EMM12-A.ASM 12 get_handle_pages EMM13-A.ASM 13 get_handle_attrib EMM19-A.ASM 19 get_handle_name EMM20-A.ASM 20 get_handle_dir EMM21-A.ASM 21 get_hw_info EMM26-D.ASM 26 get_mappable_conv_regions EMM25-A.ASM 25 get_mappable_conv_region_count EMM25-A.ASM 25 get_mappable_exp_regions EMM25-A.ASM 25 get_mappable_exp_region_count EMM25-A.ASM 25 get_mappable_regions EMM25-B.ASM 25 get_mappable_region_count EMM25-C.ASM 25 get_page_frame_count EMM25-A.ASM 25 get_page_frame_seg EMM02-A.ASM 2 get_partial_context EMM16-A.ASM 16 get_partial_context_size EMM16-C.ASM 16 get_set_context EMM15-C.ASM 15 get_total_handles EMM21-C.ASM 21 get_total_page_count EMM03-C.ASM 3 get_total_raw_page_count EMM26-C.ASM 26 get_unalloc_page_count EMM03-A.ASM 3 get_unalloc_raw_page_count EMM26-A.ASM 26 map_unmap_page EMM05-A.ASM 5 map_unmap_pages EMM17-A.ASM 17 move_memory_region EMM24-A.ASM 24 prep_EMM_warmboot EMM29-A.ASM 29 realloc_pages EMM18-A.ASM 18 restore_context EMM09-A.ASM 9 return_OS_access_key EMM30-C.ASM 30 save_context EMM08-A.ASM 8 search_handle_name EMM21-B.ASM 21 set_alt_reg_set EMM28-B.ASM 28 set_context EMM15-B.ASM 15 set_handle_attrib EMM19-B.ASM 19 set_handle_name EMM20-B.ASM 20 set_partial_context EMM16-B.ASM 16 xchg_memory_region EMM24-B.ASM 24 _________________________________________________________ Appendix A EXAMPLE PROGRAM /* The intent of this example program is to show how to store and */ /* manipulate data in expanded memory using the C Memory Manager -- */ /* MEMLIB. */ /* */ /* This example shows what developers need to do in order to manipulate */ /* expanded memory in their own applications by using MEMLIB routines. */ /* For a more detailed program that uses a doubly linked list data */ /* structure, see the ROLODEX.C program. */ /* */ /* This program follows a seven-step algorithm: */ /* 1. Check the total amount of expanded memory available. */ /* 2. Find the largest contiguous block of free expanded memory. */ /* 3. Allocate expanded memory for two strings. */ /* 4. Calculate the size of the block allocated for the first string. */ /* 5. Print the two strings. */ /* 6. Manipulate the data by appending one string to the other. */ /* 7. Print the new string. */ /**************************************************************************/ #include #include /**************************************************************************/ /* The two header files below contain the function prototypes for */ /* MEMLIB routines and the error messages specific to MEMLIB. Keep in */ /* mind that MEMLIB calls EMMLIB functions, so you may get an error code */ /* relating to EMS. (EMS codes are explained in Table A-2 in the */ /* EMS manual.) You must include these two headers for any application */ /* that will use MEMLIB. If you are going to use any EMMLIB calls */ /* directly, you must also include "emmlib.h." */ /**************************************************************************/ #include "memlib.h" #include "errors.h" #define PASSED 0 /* If the MEMLIB call was successful */ /* it will return a zero. */ #define MAX_STRING_SIZE 255 void abort (unsigned short); /* If we encounter any errors, we */ /* call this function to "clean up." */ void main() { unsigned short status; /* The status after a MEMLIB call. */ char *string1; /* Our first data item. */ char *string2; /* Our second data item. */ unsigned int token1; /* The identifier to our first */ /* block of expanded memory. */ unsigned int token2; /* The identifier to our second */ /* block of expanded memory. */ unsigned int max_block_size; /* The size of the largest */ /* allocatable contiguous block. */ unsigned int size; /* The size of one of our blocks. */ unsigned long max_exp_mem; /* The amount of expanded mem avail. */ /***********************************************/ /* See how much total expanded memory we have. */ /***********************************************/ status = ememavl (&max_exp_mem); if (status != PASSED) { printf ("Unable to obtain total expanded memory size.\n"); abort (status); } else printf ("Total expanded memory available: %lu\n", max_exp_mem); /*************************************************/ /* Get the size of the largest contiguous block. */ /*************************************************/ status = ememmax (&max_block_size); if (status != PASSED) { printf ("Unable to obtain the largest contiguous block.\n"); abort (status); } else printf ("Largest contiguous block: %u\n\n", max_block_size); /*********************************************/ /* Allocate the memory and check for errors. */ /*********************************************/ status = efmalloc (MAX_STRING_SIZE, &token1); if (status != PASSED) { printf ("Unable to allocate first block.\n"); abort (status); } else printf ("Allocated space for the first block.\n"); status = efmalloc (MAX_STRING_SIZE, &token2); if (status != PASSED) { printf ("Unable to allocate second block.\n"); abort (status); else printf ("Allocated space for the second block.\n\n"); /********************************/ /* Check the size of block one. */ /********************************/ status = emsize (token1, &size); if (status != PASSED) { printf ("Unable to obtain size for block %u\n", token1); abort (status); } else printf ("Size of block %u is %u\n\n", token1, size); /***********************************************/ /* Map in the two blocks, checking for errors. */ /***********************************************/ status = set2eptrs (token1, token2, & (char *) string1, & (char *)string2); if (status != PASSED) { printf ("Unable to map in the two blocks.\n"); abort (status); } else printf ("Blocks mapped in.\n\n"); printf ("Putting data in expanded memory.\n\n"); /*********************************************/ /* Store values in expanded memory using the */ /* pointers given to us from set2eptrs(). */ /*********************************************/ strcpy (string1, "'What if life is an illusion and nothing exists?'"); strcpy (string2, "'In that case, I definitely overpaid for my carpet.' --Woody Allen"); printf ("String1: %s\n", string1); printf ("String2: %s\n\n", string2); /**************************************************/ /* Still using the pointers, manipulate the data. */ /**************************************************/ printf ("Manipulating data in expanded memory.\n"); strcat (string1, string2); printf ("String1: %s\n\n", string1); /**************************************************************/ /* Free all expanded memory we've allocated. If effreeall() */ /* is unsuccessful, we don't want to call abort() because */ /* abort() would just call effreeall() again. */ /**************************************************************/ status = effreeall(); if (status != PASSED) { printf ("Unable to free memory.\n"); printf ("ERROR %X\n", status); exit (2); } else printf ("Expanded memory freed.\n"); } /****************************************************************************/ /* Aborts the program with an error message. Effreeall() frees all */ /* pages, blocks, and handles associated with this application. It is */ /* imperative that you release all the expanded memory you've allocated */ /* before exiting, so that other applications can use this memory. If */ /* you don't, you'll have to reboot to recover the lost memory. Notice */ /* also that a call to effreeall() checks the EMM status. In case that */ /* call fails, you'll get an error condtion back to let you know the */ /* memory was not freed. */ /* */ /* You should always call effreeall() before aborting your program, even */ /* if you get an error before allocating any memory. Effreeall() will free */ /* the handles and memory that MEMLIB allocated when it was initialized. */ /****************************************************************************/ void abort (status) unsigned short status; { printf ("ERROR %X\n", status); status = effreeall(); if (status != PASSED) { printf ("ERROR %X from effreeall().\n"); printf ("Expanded memory not freed.\n"); } exit (1); } Appendix B MEMLIB ERROR MESSAGES This appendix lists the MEMLIB error codes that may appear when you are running your application. The codes are listed in numerical order. Each error code description includes: o the hexadecimal number of the error code o a one-line description of the error o a short explanation of why the error message appeared o what you should do to correct the error Because MEMLIB makes calls to EMMLIB functions, you may see messages other than those listed here. Each EMMLIB error message includes a hexadecimal code number used by the Expanded Memory Specification (EMS). To find out more about these errors, look up the code number in Table A-2, "Status and Function Code Cross Reference," in the EMS manual. Error: 0xD0 INVALID_TOKEN cause The token that the MEMLIB routine received did not represent an allocated block of memory. action Each time efmalloc allocates a block of memory, it returns a token unique to that block. Use this token when referring to a particular block. 0xD1 NOT_ENOUGH_UNALLOCATED_PAGES cause Your computer does not have enough free expanded memory. MEMLIB requires at least 1 page (16K bytes) for its internal directory, and assumes that you'll need at least 1 more page to store data for your application. action You need more expanded memory. Add more memory to your Above Board, buy a Piggyback Option, or buy another Above Board. 0xD2 TOO_MANY_DIRECTORY_ENTRIES cause You requested more separate blocks of memory than the MEMLIB directory can handle. The maximum number of directory entries available is 65535. action Try requesting a few larger blocks instead of many small blocks. 0xD3 REQUEST_FOR_ZERO_LENGTH_BLOCK cause You tried to allocate a 0K-byte block of memory. action When you use efmalloc, make sure the size parameter is greater than zero. 0xD4 CANNOT_MAP_ALL_BLOCKS cause The blocks you attempted to access can't all be mapped into the page frame at the same time. action Try accessing fewer blocks at a time. See the NOTE at the beginning of the seteptrs function for more information on the number of memory blocks you can access. 0xD5 MAX_PUSH_CONTEXTS_EXCEEDED cause MEMLIB has a set maximum number of contexts it will "push" onto a stack, and you have exceeded that maximum. (See Function 11 for more information about contexts.) action You can either pop some of the contexts before pushing any more, or you can change the maximum default. In the MEMINTRL.C file you'll find a #define called MAX_CONTEXTS_AVAILABLE. Increase this number, then recompile MEMLIB. 0xD6 MALLOC_FAILURE cause When you tried to push a context, MEMLIB could not find enough open conventional memory to hold the context. action Use the pop_context function to release the contexts you have already pushed. If you haven't called push_context previously but get this error, you don't have enough free conventional memory to use the push_context function. Unless you can free some of your computer's conventional memory, don't call push_context or pop_context. 0xD7 NO_CONTEXT_AVAILABLE_TO_POP cause You attempted to call pop_context before calling push_context, or you tried to pop a context you had popped already. action Use the push_context function before using pop_context. 0xD8 ERROR_REALLOCATING_PAGES cause Efmalloc could not allocate the block of memory you requested because your computer doesn't have enough free expanded memory. action Allocate a smaller block of memory, add more memory to your Above Board, or buy another. 0xD9 NOT_INITIALIZED cause You called a function that was designed to be called by another function, not by your application. action Limit your MEMLIB function calls to the functions listed in this manual. Appendix C TECHNICAL INFORMATION ABOUT MEMLIB This appendix explains in detail how MEMLIB provides access to expanded memory, how it keeps track of expanded memory, how it allocates memory, and the algorithms it uses. You don't need this information to use MEMLIB with your application program. It's provided for your information only. If you'd like more information about how expanded memory works, read Chapter 1 in the EMS manual. How MEMLIB provides access to expanded memory --------------------------------------------- You can have up to 32M bytes of expanded memory in your computer, and MEMLIB can provide access to all of it. Expanded memory is divided into logical pages. Each logical page is 16K bytes. To access a block of expanded memory, MEMLIB needs to map in the logical page that the block uses. MEMLIB can map only a limited number of these pages into the EMS page frame at one time. For example, if your page frame is 64K bytes, four logical pages can be mapped into it. You can access four memory blocks at the same time if the size of each block is 16K bytes or less. However, accessing five blocks at once may be impossible, because the blocks might use five different logical pages (five pages won't fit in the page frame). How MEMLIB keeps track of expanded memory ----------------------------------------- The expanded memory manager, MEMLIB, is based on a directory structure. Each directory entry consists of four fields: unsigned int token (0xFFFF = free, otherwise = allocated) unsigned int size (size [in bytes] of this block) unsigned short offset (the offset into the first logical page of this block) int logical_page[4] (pages used) Blocks less than or equal to 16K bytes will use one logical page; 16 - 32K, two pages; 32 - 48K, three pages; and 48 - 64K, four pages. This directory is stored in expanded memory that MEMLIB allocates when needed. How MEMLIB allocates expanded memory ------------------------------------ To allocate memory, MEMLIB attempts to reuse as much deallocated memory as possible before allocating any new blocks. When a block of memory is freed, MEMLIB checks all other freed blocks to see if they fall immediately before or after that block. If there are any adjacent free blocks, MEMLIB combines the blocks into one larger block. When a user requests a block of memory, MEMLIB checks all free blocks and gives the user the smallest free block bigger than the size requested (best fit). Any leftover memory is put into another free block. EXAMPLE: A user makes an efmalloc request for a 6K block. MEMLIB allocates logical page 1 from EMM, and creates two directory entries: one to the 6K block and one to the 10K free block. Next, the user makes another efmalloc request, this time for an 11K block. 11K won't fit in the 10K free block in logical page 1, and will overlap into the next logical page if assigned to page 1 at all. MEMLIB allocates an entirely new logical page, page 2. Again, it creates one directory entry to point to the allocated block, and another entry to point to the 5K of free memory (4). Algorithms ---------- When a user requests a block of memory (efmalloc), Intel uses the following algorithm: (C pseudo code) do if (exact fit) allocate block else check for best fit while ((not exact fit) and (not last directory entry)) if (not exact fit) if (best fit) allocate block set directory entry for remainder else request page from expanded memory manager if request = OK allocate block else return NOT_ENOUGH_UNALLOCATED_PAGES error To free a block of memory (effree), we use this algorithm: do if (contiguous free block) combine into one free block while (not last directory entry) if (no allocated pages found) deallocate all pages /* prepare for program termination */ When mapping in blocks of memory (seteptrs), we use the following algorithm: for (i = 0, i < number of requested blocks; i++) if (logical pages needed for block i are already mapped) OK = true else if (pages need for block i will fit) OK = true map in pages needed else return CANNOT_MAP_ALL_BLOCKS error