FORCE FAQ (Frequently Asked Questions) (xms.faq 1.1) 1 ------------------------------------------------------------------ Advanced Topic: Interfacing with an XMS driver Author: David Holmes Examples: Xstat.exe, Show.exe General discussion: ----------------------------------------------------------------- Q: How do I access XMS memory from a FORCE program? A: Through the driver. Unfortunately, the driver is meant to be called by assembler functions, not by higher level languages such as FORCE. However, it's easy to get around that, because FORCE takes to assembly language like a duck to water. Q: I don't know assembler, so what can a guy do? A: Well, you could learn it, but for most people, that's just not a viable option, since it does take a while. But, you won't have to, because provided here in this special chapter of the FAQ is a function called (very cleverly): call_driver() Call_driver() is an assembly language "wrapper" function, just like the undocumented function Interrupt(). In order to use it, you should know something about assembly language, mainly; what registers are, and what they do. If you don't, don't bother, because I've also provided FORCE wrapper functions around the Call_Driver() function, which allow you to access XMS memory in a more readable, higher-level interface. But, let's take a look at the Call_Driver() function just for reference. It's prototype looks like this: FUNCTION LOGICAL call_driver PROTOTYPE PARAMETERS VALUE LONG drv_address,; UINT ax, UINT bx,; UINT cx, UINT dx,; UINT si, UINT di,; UINT bp, UINT ds,; UINT es If you know assembler, you'll see that all the 8086 registers (with the exceptions SS, SP, CS, and IP) are passed in as parameters. Of course, they're not really the registers, but their values are loaded into the corresponding registers before the driver is called. If you load up the registers like so: VARDEF UINT ax,bx,cx,dx,si,di,bp,ds,es ENDDEF ax = 0x0800 && Service for querying available XMS -------------------------------------------------------------------- 1 FORCE FAQ (Frequently Asked Questions) 2 -------------------------------------------------------------------- You can call the driver with the register AX being 0x0800. call_driver( xms_get_driver(), ax, bx, cx, dx, si, di,; bp, ds, es ) The specifications for the XMS driver are such that for this function, "return available extended memory in Kilobytes," the available XMS will come back in DX. So, after the call to Call_driver(), you can look at your DX variable to see how much XMS memory there is. Simple as that. You'll find that the call_driver() function mimics the Interrupt() function finally documented in UNDOC.FAQ. ------------------------------------------------------------------ Q: Hm. Still looks suspiciouly like assembler to me. What do you have the I can USE? A: As promised, I've provided several wrapper functions that hide the Call_driver() function from you, so that you don't have to worry about it if you don't want to. Let me introduce them. FUNCTION LOGICAL xms_installed FUNCTION LONG xms_get_driver FUNCTION UINT xms_get_version FUNCTION INT xms_avail FUNCTION LOGICAL xms_alloc PROCEDURE xms_free FUNCTION LOGICAL xms_copy FUNCTION LONG xms_lock FUNCTION INT xms_unlock * Plus two wrapper functions around xms_copy() FUNCTION LOGICAL xms_insert FUNCTION LOGICAL xms_retrieve To use these functions, you'll need a little background on the Extended Memory Specification. First, the basics: XMS is the memory area defined as "anything above 1M," and blocks of memory that reside in the XMS are referred to as Extended Memory Blocks (EMB's). Memory that resides between DOS 640K and the 1M mark are called Upper Memory Blocks (UMB's), and may or may not be supported by the XMS driver (Himem.sys does NOT support them). Because EMB's are above the 1M mark, you cannot access them as you would with normal memory. This is because DOS pointers are 20 bits in length, giving you an address space of 1,048,576 bytes, or roughly 1 Megabyte. This is why the driver gives you a ``handle'' instead of a pointer when you allocate an EMB. With this handle, you can ask the driver to copy memory below the 640K mark into the EMB, and vice versa. -------------------------------------------------------------------- 2 FORCE FAQ (Frequently Asked Questions) 3 -------------------------------------------------------------------- So, that's what all this is about, now let's see how you can do it with the functions provided with this chapter. There are four functions that you'll use heavily when working with XMS in FORCE. They are xms_alloc(), xms_free(), xms_insert(), and xms_retrieve(). The last two are just wrappers around the xms_copy() function, and the first two you'll need to get and free the XMS memory. The rest of the functions are used internally by these four functions. For example, you don't need to worry about the xms_get_driver() function, since it just returns the address of the driver, and that's taken care of by all of the functions in the XMS library. Let's suppose you wanted a slick way to save entire screens to some convenient place, and bring them back at will. You could do something like this: VARDEF INT temp_handle LONG video_mem = 0xb8000000 ENDDEF PROCEDURE screen_to_xms xms_alloc( temp_handle, 4 ) && screens are 4K xms_copy( &NULL, video_mem, temp_handle, 0, 4000 ) ENDPRO Then, when you want your screen back: PROCEDURE xms_to_screen xms_copy( temp_handle, 0, &NULL, video_mem, 4000 ) xms_free( temp_handle ) ENDPRO Or, suppose that you wanted a way to read a "screen" from disk, copy it into XMS, and then call it up whenever you want. You could do something like this: VARDEF BYTE one_screen[4000] INT help_handle ENDDEF PROCEDURE read_help_screen PARAMETERS CONST CHAR filename VARDEF UINT file_handle ENDDEF -------------------------------------------------------------------- 3 FORCE FAQ (Frequently Asked Questions) 4 -------------------------------------------------------------------- fb_open( file_handle, filename, &B_READ ) fb_read( file_handle, one_screen, 4000 ) fb_close( file_handle ) xms_alloc( help_handle, 4 ) xms_insert( one_screen, help_handle, 0, 4000 ) ENDPRO In fact, there's a sample program that shows you exactly how you might want to implement this in the SAMPLE directory. Well, that's all for now. I am of course, always available on the BBS and CIS, so if you have any questions or comments, feel free to contact me. -------------------------------------------------------------------- 4