EASYX PROGRAMMER'S MANUAL VERSION 1.2 (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. Initialization of EASYX 2 3. Memory Management 3 4. Memory-Mapped Input/Output 5 5. File Management 6 EXAMPLES Page 1. EASYX Memory Management 4 2. EASYX File Management 9 1. Introduction EASYX is a DOS extender library intended for programmers who are unfamiliar with assembly language. EASYX allows real-mode high-level languages to perform transfers between extended memory and conventional memory or between extended memory and disk files. Many programmers need a DOS extender merely to afford real-mode languages access to extended memory. In such cases, EASYX is a simple and powerful alternative. EASYX is actually little more than a real-mode interface to XLIB. The source code for EASYX is supplied in the XLIB archive (EASYX.ASM). Assembly language programmers may be interested in examining this code to learn more about working with XLIB. This code has been assembled and then linked with XLIB.LIB to create EASYX.LIB. The EASYX library is provided in two formats - one for Microsoft languages and the other for Borland languages. Microsoft programmers should use EASYX.LIB. Borland programmers should use EASYXB.LIB. The respective C header files are EASYX.H and EASYXB.H. Programmers using other languages will have to write their own prototypes for EASYX procedures. These prototypes should declare all EASYX procedures as far procedures conforming to the PASCAL calling and naming convention. Prototypes for Microsoft BASIC 7.0 are presented in the files EASYXEX1.BAS and EASYXEX2.BAS. The programmer might prefer to use EASYXE.LIB rather than EASYX.LIB. The former has CPU exception trapping capabilities whereas the latter does not. EASYXE.LIB will always trap exceptions occurring in protected mode. It can also trap real-mode exceptions provided that a DPMI 1.0 host is installed. Borland programmers should use EASYXEB.LIB. C and C++ programmers should continue to use EASYX.H or EASYXB.H for header files. Most EASYX procedures can return error codes. These are always XLIB error codes. The codes are explained in the XLIB documentation. 1 2. Initialization of EASYX EASYX requires no initialization; however, the XLIB library which has been linked with EASYX must be initialized before any EASYX procedures are called. XLIB is initialized by calling INITXLIB. INITXLIB returns an error code in DX:AX; therefore, it should be implemented as a long integer function. The following code demonstrates initialization in Microsoft C: extern long __far __pascal INITXLIB(void); void main(void) { long errcode; errcode = INITXLIB(); if(errcode != 0) { printf("Initialization error: %lX",errcode); return; } . . . An explanation of possible error codes is explained in the XLIB documentation. The most common error is caused by insufficient conventional memory. INITXLIB will attempt to allocate a small amount of conventional memory through DOS; however, many high-level languages automatically claim all available DOS memory, even though only a small percentage of this memory may actually be used. The programmer must therefore release a portion of this memory back to DOS before calling INITXLIB. This process is illustrated for the Borland Turbo Assembler in the file EXAMP1B.ASM, and for Microsoft BASIC in EASYXEX1.BAS. C and C++ programmers need not be concerned with this matter because these languages allocate DOS memory dynamically. 2 3. Memory Management High-level languages confined to real mode are incapable of addressing extended memory; consequently, such languages must communicate with extended memory through buffers in conventional memory. EASYX provides several procedures to facilitate this process. Extended memory should always be allocated with the procedure XMALLOC before it is addressed. Attempted transfers to or from unallocated memory may lead to page faults (exception 14), or to corruption of resident software. XMALLOC returns a long integer error code; therefore, it should be implemented as a long integer function. The following Microsoft C prototype shows the structure of XMALLOC: extern long __far __pascal XMALLOC(long nobytes, long __far *address, long __far *size, long __far *handle); where: nobytes = The requested size for the extended memory block. *address = A far pointer to a long integer variable which will receive the linear address of the allocated block. *size = A far pointer to a long integer variable which will receive the actual size of the allocated extended memory block. The actual size will always be at least as large as the requested size. *handle = A far pointer to a long integer variable which will receive a handle for the allocated extended memory block. The handle must be used to release the block. An extended memory block can be released with the XFREE procedure. XFREE is also a long integer function which returns an error code. The C prototype of this procedure is: extern long __far __pascal XFREE(long handle); where: handle = The handle assigned to the block by XMALLOC. All extended memory is automatically released upon program termination; consequently, XFREE will not be necessary for most programs. Transfers between conventional memory and extended memory can be accomplished with the MOVMEM procedure. The prototype for MOVMEM is: extern void __far __pascal MOVMEM(long destadr, long sourceadr,long nobytes); where: destadr = The linear address of the destination memory. sourceadr = The linear address of the source memory. nobytes = The number of bytes to be transferred. 3 MOVMEM may actually be used to transfer memory between any source and destination. The destination block and source block may also be overlapped. MOVMEM transfers are faster than XMS or INT 15H because MOVMEM uses 32- bit instructions. MOVMEM also exposes to less risk of losing an interrupt. MOVMEM is a reentrant procedure, so it may be safely called within interrupt handlers. When MOVMEM is being used in a hardware interrupt handler, the handler should be installed after the call to INITXLIB to prevent a call to MOVMEM before protected-mode structures have been initialized. Real-mode programs use segment addresses instead of linear addresses. EASYX includes a procedure called LINADR which computes linear addresses from segment addresses. The prototype for LINADR is: extern long __far __pascal LINADR(void __far *ptr); The following program uses the procedures presented in this section. The file EASYXEX1.BAS contains a Microsoft BASIC 7.0 version of the program. Example 1: EASYX Memory Management _____________________________________________________________________________ #include #include void main(void) { long errcode, nobytes, xaddress, xsize, xhandle, bufferaddress; long buffer[1024]; /*4k buffer*/ errcode = INITXLIB(); /*Initialize XLIB*/ if(errcode != 0) { printf("Library initialization error: %lX\n",errcode); return; } nobytes = 0x10000; /*Allocate 64k of extended memory*/ errcode = XMALLOC(nobytes, &xaddress, &xsize, &xhandle); if(errcode != 0) { printf("Memory allocation error: %lX\n",errcode); return; } bufferaddress = LINADR(buffer); /*Get linear address of buffer*/ MOVMEM(xaddress, bufferaddress, 4096); /*Transfer buffer to extended*/ MOVMEM(bufferaddress, xaddress, 4096); /*Transfer extended to buffer*/ errcode = XFREE(xhandle); /*Release the extended memory*/ if(errcode != 0) printf("Memory release error: %lX\n",errcode); } _____________________________________________________________________________ 4 4. Memory-Mapped Input/Output On 386 and higher machines, memory reads and writes may undergo address translation so that the address which is physically accessed may be different from the address specified in the read/write instruction. The physically accessed address is the "physical address." The address specified by the read/write instruction is the "logical address." Normally programmers need not be concerned with the difference; however, this is not the case if the program must access an IO device which maps to a fixed physical address. In such cases, the programmer must obtain a logical address which corresponds to the physical address of the IO device. EASYX contains a procedure called MAPIOMEM which will map a specified physical address into a logical address space. MAPIOMEM is a long integer function which returns an error code. The prototype for MAPIOMEM is: extern long __far __pascal MAPIOMEM(long physaddress, long size, long __far *logaddress); where: physaddress = The beginning physical address for the IO device. size = The size of the physical address block in bytes. *logaddress = A far pointer to a long integer which is to receive the assigned logical address. Access the IO device at this address. MAPIOMEM will sometimes return errors upon attempts to map physical addresses in the first megabyte. 5 5. File Management EASYX includes procedures which can transfer data between extended memory and files. These procedures can load files to extended memory or save extended memory to files. They can read and write files either sequentially or randomly. All EASYX 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 following form: 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 source/destination block in bytes BUFADR DWORD Buffer address (conventional memory) BUFSIZE WORD Buffer size in bytes CONTROL WORD Control word CONDCODE is used to return error codes. CONDCODE should be situated at the starting address of the control block. FNAME is a zero-terminated ASCII string defining the file path and name. There cannot be more than 68 characters in this string, including 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 intrafile 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) 6 CONTROL is not used in the present version of EASYX. All bits in control should be set to zero. 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 EASYX to supply a buffer when opening or creating the file. EASYX file routines should not be called within interrupt handlers because all of these routines use DOS. All EASYX file routines receive a single argument; namely, the far address of the control block. Prototypes for the file routines are: extern void __far __pascal XFCREATE(void __far *controlblock); extern void __far __pascal XFOPEN(void __far *controlblock); extern void __far __pascal XFCLOSE(void __far *controlblock); extern void __far __pascal XFLOAD(void __far *controlblock); extern void __far __pascal XFSAVE(void __far *controlblock); extern void __far __pascal XFREAD(void __far *controlblock); extern void __far __pascal XFWRITE(void __far *controlblock); The following is a detailed explanation of each of these procedures: XFCREATE (Create File) Purpose: Create and open a new file of specified name in specified directory. Control Block at Call: FNAME = file path and name. 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 EASYX will set BUFADR and BUFSIZE to its own internal buffer. Details: If the file already exists, then it will be truncated to zero length. Files created by this routine will be given both read and write access. XFOPEN (Open File) Purpose: Open existing file of specified name in specified directory. Control Block at Call: FNAME = file path and name. 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 EASYX will set BUFADR and BUFSIZE to its own internal buffer. Details: The file is opened for both read and write access. XFCLOSE (Close File) Purpose: Close previously opened file. Control Block at Call: FHANDLE = file handle. Control Block at Return: CONDCODE = error code. 7 XFSAVE (Save File) Purpose: Create file with contents equal to specified memory 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. 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 EASYX will automatically supply a buffer. XFLOAD (Load File) Purpose: Load file contents to specified memory 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. Control Block at Return: CONDCODE = error code. If CONDCODE = 0, then BLKSIZE = actual number of bytes transferred. Details: The file cannot already be open. The file is both opened and closed by this routine. The value of BLKSIZE as of call is interpreted as an upper limit on the number of bytes to transfer. The entire file is loaded provided that it does not contain more than BLKSIZE bytes. 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 EASYX will automatically supply a buffer. XFWRITE (Write to File) Purpose: Write specified memory block to specified location in open file. 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. Control Block at Return: CONDCODE = error code. Details: The file must be opened with XFOPEN or XFCREATE before using this routine. BLKADR/BLKSIZE may define a conventional memory block provided that this block is not overlapped by BUFADR/BUFSIZE. Sequential transfers should set FPTRMODE = 3 for fastest execution. XFREAD (Read From File) Purpose: Load specified memory block from specified location in open file. 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. Control Block at Return: CONDCODE = error code. If CONDCODE = 0, then BLKSIZE = the actual number of bytes transferred. 8 Details: The file must be opened with XFOPEN or XFCREATE before using this routine. BLKADR/BLKSIZE may define a conventional memory block provided that this block is not overlapped by BUFADR/BUFSIZE. Sequential transfers should set FPTRMODE = 3 for fastest execution. The following C program illustrates the usage of some of the above procedures. A BASIC translation of this program is in EASYXEX2.BAS. Example 2: EASYX File Management _____________________________________________________________________________ #include #include #include void main (void) { int i; long errcode, nobytes, xaddress, xsize, handle, arrayaddress; int array[100]; struct xfile fb; /*Declare file control block*/ errcode = INITXLIB(); /*Initialize XLIB*/ if(errcode != 0) { printf("Library initialization error: %lX\n",errcode); return; } nobytes = 0x10000; /*Allocate 64k of extended memory*/ errcode = XMALLOC(nobytes,&xaddress,&xsize,&handle); if(errcode != 0) { printf("Extended memory allocation error: %lX\n",errcode); return; } for(i = 0; i < 100; i++) /*Put something in array[]*/ array[i] = i; arrayaddress = LINADR(array); /*Compute linear address of array[]*/ 9 fb.condcode = 0; /*Set control block to create file*/ strcpy(fb.fname,"junk.dat"); /*Specify file name*/ fb.blkadr = arrayaddress; /*Will transfer array[] to the file*/ fb.blksize = 200; /*There are 200 bytes in array[]*/ fb.bufsize = 0; /*Force XLIB to use its internal buffer*/ XFSAVE(&fb); /*Create file and save array[] to it*/ if(fb.condcode != 0) { printf("File save error: %lX\n",fb.condcode); return; } XFOPEN(&fb); /*Reopen the file*/ if(fb.condcode != 0) { printf("File open error: %lX\n",fb.condcode); return; } fb.blkadr = xaddress; /*Prepare to transfer the file to extended*/ fb.blksize = 100; /*Will transfer only 100 bytes*/ fb.fptrmode = 0; /*File pointer is relative to start of file*/ fb.fptr = 100; /*Set file pointer to 50th element*/ XFREAD(&fb); /*Read last 50 elements to extended*/ if(fb.condcode != 0) { printf("File read error: %lX\n",fb.condcode); return; } MOVMEM(arrayaddress,xaddress,100); /*Transfer file contents back to array[] XFCLOSE(&fb); /*Close the file*/ if(fb.condcode != 0) { printf("File close error: %lX\n",fb.condcode); return; } errcode = XFREE(handle); /*Release extended memory*/ if(errcode != 0) { printf("Memory release error: %lX\n",errcode); return; } } _____________________________________________________________________________ 10