// DW: This file taken from Borland's tech support site & modified

/*************************************************************
	This program shows how to use XMS.  In this example, we
	simulate a large array in XMS and show that it works by
	writing values into it and reading them out again.
	Compile in any model but HUGE and make sure you have
	HIMEM.SYS or another XMS manager loaded.
	Many thanks to Ray Duncan, whose book "Extending DOS"
	(written with many others) was very helpful.
************************************************************/

#ifdef __HUGE__
//        #error This example cannot be compiled in the huge memory model.
#endif

#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <alloc.h>
#include "xms.h"

#define TRUE 1
#define FALSE 0

// Blocksize will be the size of our real-memory buffer that
// we'll swap XMS through (must be a multiple of 1024, since
// XMS is allocated in 1K chunks.) Can be > 64K as implemented

long Blocksize;

// XMSParms is a structure for copying information to and from
// real-mode memory to XMS memory

struct parmstruct
{
	// blocklength is the size in bytes of block to copy

	unsigned long blockLength;

	// sourceHandle is the XMS handle of source; 0 means that
	// sourcePtr will be a 16:16 real-mode pointer, otherwise
	// sourcePtr is a 32-bit offset from the beginning of the
	// XMS area that sourceHandle points to

	unsigned int sourceHandle;
	far void *sourcePtr;

	// destHandle is the XMS handle of destination; 0 means that
	// destPtr will be a 16:16 real-mode pointer, otherwise
	// destPtr is a 32-bit offset from the beginning of the XMS
	// area that destHandle points to

	unsigned int destHandle;
	far void *destPtr;
} XMSParms;

// XMSFunc is a function pointer we'll use to call into the XMS
// manager
void far (*XMSFunc) (void);
// XMSBuf is the real-mode memory buffer for transfers to and
// from XMS.  Make it a huge * instead of a far * if Blocksize
// is >= 64K
unsigned long huge *XMSBuf;
// XMSHandle is the handle we'll use to refer to our
// XMS allocation
// when talking to the XMS manager
unsigned int XMSHandle;

// a flag which says whether the current buffer has been
// written into or not
char curBlockDirty=FALSE;
// the part of our XMS allocation that is currently copied
// into real memory
int curBlock=-1;

// returns TRUE if XMS present, FALSE otherwise
char XMSCheck(void)
{
	_AX=0x4300;
	geninterrupt(0x2F);
	return (_AL==0x80);
}

// GetXMSEntry sets XMSFunc to the XMS Manager entry point
// so we can call it later

void GetXMSEntry(void)
{
	_AX=0x4310;
	geninterrupt(0x2F);
	XMSFunc= (void (far *)(void)) MK_FP(_ES,_BX);
}

// XMSSize returns the total kilobytes available, and the size
// in kilobytes of the largest available block

void XMSSize(int *kbAvail, int *largestAvail)
{
	_AH=8;
	(*XMSFunc)();
	*largestAvail=_DX;
	*kbAvail=_AX;
}

char AllocXMS(unsigned long numberBytes)
{
	int numBlocks;
	// divide by Blocksize to get number of blocks
	numBlocks=(int) (numberBytes/Blocksize);
	if ((numberBytes%Blocksize) != 0) ++numBlocks;
	_DX=(int) (numBlocks*(Blocksize/1024));
	_AH=9;
	(*XMSFunc)();
	if (_AX==0)
	{
		char buf[150];
		sprintf(buf,"Could not allocate %lu bytes of XMS memory",
		(long)numBlocks*Blocksize);
		XMSError(buf);
		return FALSE;
	}
	XMSHandle=_DX;
	return TRUE;
}

// XMSFree() frees up the XMS handle that we allocated

void XMSFree(void)
{
	curBlockDirty=FALSE;
	curBlock=-1;
	if( !XMSBuf ) return;
	_DX=XMSHandle;
	_AH=0x0A;
	(*XMSFunc)();
	XMSBuf = 0L;
}

// CleanUpAndLeave() frees up the XMS handle that we allocated

void CleanUpAndLeave(void)
{
	XMSFree();

}

// GetCorrectBlock() makes sure that the current block stored
// in the buffer XMSBuf is the correct block by swapping the
// current block out and bringing in a new block

void GetCorrectBlock(unsigned int block)
{
	// curBlockDirty is checked for an optimization:  we write
	// out the current buffer back out to XMS only if it's
	// "dirty" (been written into).
	if (curBlockDirty)
	{
		// write dirty block from XMSBuf out to XMS
		//x printf("Writing out used block %d\n", curBlock);
		XMSParms.sourceHandle=0;
		XMSParms.sourcePtr=XMSBuf;
		XMSParms.destHandle=XMSHandle;
		XMSParms.destPtr=(void far *) (((long)curBlock) * Blocksize);
		XMSParms.blockLength=Blocksize;
		// pass a pointer to the parameters structure to
		// the XMS Manager
		_SI=FP_OFF(&XMSParms);
		_AH=0x0B;
		(*XMSFunc)();
		if (_AX==0)
		{
			char buf[100];
			sprintf(buf,"FATAL: Error writing XMS block %d!",curBlock);
			XMSError(buf);
			CleanUpAndLeave();
		}
	}
	// fetch block from XMS into XMSBuf buffer
	//x printf("Getting XMS block %d\n",block);
	XMSParms.sourceHandle=XMSHandle;
	XMSParms.sourcePtr=(void far *) (((long)block) * Blocksize);
	XMSParms.destHandle=0;
	XMSParms.destPtr=XMSBuf;
	XMSParms.blockLength=Blocksize;
	_SI=FP_OFF(&XMSParms);
	_AH=0x0B;
	(*XMSFunc)();
	if (_AX==0)
	{
	
		CleanUpAndLeave();
	}
	curBlockDirty=FALSE;
	curBlock=block;
}

// DW: copy the location "loc" into the real buffer, "dirty" means we're about to change something at this loc
unsigned long XMSCurrent(unsigned long loc, int dirty)
{
	unsigned int block;
	if( !XMSBuf ) return loc;
	block=(int) (loc/Blocksize);
	if (block != curBlock) GetCorrectBlock(block);
	curBlockDirty |= dirty;
	return loc - (unsigned long)block*Blocksize;
}

int AllocateXMS( void *realBuf, long realSize, long XMSLen )
{
//                       unsigned int kbAvail,largestAvail;

	if (!XMSCheck())
	{
		XMSError("XMS memory is not available. Cannot use EPROMs this large.");
		return(1);
	}

	GetXMSEntry();
	XMSFree();

//                       XMSSize(&kbAvail,&largestAvail);
//                       printf("Kilobytes Available: %d; Largest block: %uK\n",
//                                                               kbAvail,largestAvail);

	XMSBuf = (unsigned long huge *)realBuf;
	Blocksize = realSize;

	if (!AllocXMS(XMSLen)) {
		XMSBuf = 0L;
		return(1);
	}

	return(0);
}


