	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
