#ifdef MSDOS
#pragma inline

#include "xmsmgr.hpp"

xmsmgr::xmsmgr()
{
	xmsaddr = 0L;		// assume not present at first
	installed = FALSE;
	largest_free_xm = 0;
	total_free_xm = 0;
	errcode = 0;
				// this one is tricky
				// look at the pointer cast carefully

	list_handles = (unsigned (*)[128][2])new unsigned[128*2];
	int i;
	for (i = 0; i < 128; ++i)
		(*list_handles)[i][0] = -1;
	asm	{		// test for the HIMEM.SYS driver
		mov ax,4300h	// 4300h = get install state
		int 2fh		// call driver
		cmp al,80h	// AL has 80h if present
		je  is_installed
		jmp not_installed
	}
is_installed:
	long driver = xmsaddr;

	asm	{
		mov ax,4310h		// request driver address
		int 2fh			// call driver
		mov word ptr driver,bx	// driver's far call address
		mov word ptr driver+2,es
	}
	xmsaddr = driver;
	installed = TRUE;
	query_xms();
	if (errcode)
		installed = FALSE;
	return;

not_installed:
	installed = FALSE;
}

xmsmgr::~xmsmgr()
{
	long driver = xmsaddr;
	int i;
	for (i = 0; i < 128; ++i)	{
		unsigned emb = (*list_handles)[i][0];
		if (emb != -1)	{	// if emb slot used
			asm	{
				mov ah,0ah	// free emb
				mov dx,word ptr emb
				call dword ptr driver
			}
		}
	}
	delete list_handles;
}

void xmsmgr::query_xms()
{
	long driver = xmsaddr;
	unsigned x_largest = largest_free_xm;
	unsigned x_total = total_free_xm;

	asm	{
		mov ah,08h
		call dword ptr driver
		or ax,ax
		jz badcall
		mov word ptr x_largest,ax
		mov word ptr x_total,dx
	}
	largest_free_xm = x_largest;
	total_free_xm = x_total;
	return;
badcall:
	errcode = _BL;		// store XMS error state
}

char* xmsmgr::xmserr()
{
	static char errstring[80];

	strcpy (errstring, "XMS Memory error: ");
	switch (errcode)	{
		case notimpl:	strcat(errstring,"function not implemented.");
				break;
		case vdisk:	strcat(errstring,"VDISK device driver present.");
				break;
                case a20err:    strcat(errstring,"A20 error.");
                                break;
                case generr:    strcat(errstring,"General driver error.");
                                break;
                case unrecov:   strcat(errstring,"Unrecoverable driver error.");
                                break;
                case nohma:     strcat(errstring,"HMA does not exist.");
                                break;
                case hmainuse:  strcat(errstring,"HMA is in use.");
                                break;
                case hmamin:    strcat(errstring,"Req. less than HMAMIN.");
                                break;
                case hmanotal:  strcat(errstring,"HMA is not allocated.");
                                break;
                case a20enab:   strcat(errstring,"A20 line still enabled.");
                                break;
                case allxms:    strcat(errstring,"All XM is allocated.");
                                break;
                case emmhandl:  strcat(errstring,"No more EMM handles.");
                                break;
                case invhandl:  strcat(errstring,"Invalid handle.");
                                break;
                case srchandl:  strcat(errstring,"Invalid source handle.");
                                break;
                case srcoffs:   strcat(errstring,"Invalid source offset.");
                                break;
                case dsthandl:  strcat(errstring,"Invalid destination handle.");
                                break;
                case dstoffs:   strcat(errstring,"Invalid destination offset.");
                                break;
                case invlen:    strcat(errstring,"Invalid length.");
                                break;
                case overlap:   strcat(errstring,"Blocks for move overlap.");
                                break;
                case parity:    strcat(errstring,"XMS parity error.");
                                break;
                case notlock:   strcat(errstring,"Block is not locked.");
                                break;
                case islock:    strcat(errstring,"Block is locked.");
                                break;
                case lockcnt:   strcat(errstring,"Exceeded max locks.");
                                break;
                case lockfail:  strcat(errstring,"Lock request failed.");
                                break;
                case smallumb:  strcat(errstring,"A smaller UMB is available.");
                                break;
                case noumb:     strcat(errstring,"No UMBs available.");
                                break;
                case umbseg:    strcat(errstring,"Invalid UMB segment number.");
                                break;
                case xmshandl:  strcat(errstring,"No more XMS handles.");
                                break;
	}
	return errstring;
}

int xmsmgr::alloc_emb (size_t kbytes)
{
	int i;

	for (i = 0; i < 128; ++i)	{
		if ((*list_handles)[i][0] == -1)	{	// unused slot
			long driver = xmsaddr;
			asm	{
				mov ah,09h		// request allocate
				mov dx,word ptr kbytes
				call dword ptr driver
				or ax,ax
				jz badnews
				jmp goodnews
			}
goodnews:
			(*list_handles)[i][0] = _DX;	// save handle
			(*list_handles)[i][1] = kbytes;
			return i;	// return list element, not handle
badnews:
			errcode = _BL;		// save error code
			installed = FALSE;	// and signal error
			return -1;
		}
	}
	// If you fall through, no more xms handles in list.

	errcode = xmshandl;
	installed = FALSE;
	return -1;
}


void xmsmgr::free_emb (int listnum)
{
	if (listnum >= 128 || (*list_handles)[listnum][0] == -1)
		return;
	unsigned handle = (*list_handles)[listnum][0];
	long driver = xmsaddr;
	asm	{
		mov ah,0ah		// request deallocate
		mov dx,word ptr handle
		call dword ptr driver
	}
}

Boolean xmsmgr::stow (char far* send, size_t listnum,
	unsigned long bytes, unsigned long ofs)
{
	if (listnum >= 128)	{
		errcode = invhandl;
		installed = FALSE;
		return FALSE;
	}

	xmsparms pstow;

	if (bytes % 2)
		++bytes;
	pstow.emb_length = bytes;
	pstow.source_handle = 0;
	pstow.source_offset = (long)send;
	pstow.dest_handle = (*list_handles)[listnum][0];
	pstow.dest_offset = ofs;
	long driver = xmsaddr;
	asm	{
		push ds			// be very careful with DS
		push si
		mov ah,0bh		// request EMB MOVE
		lea si,pstow		// do SI first, about to lose DS
		mov dx,ss
		mov ds,dx
		call dword ptr driver	// driver on stack, so it works
		pop si			// be sure to recover these regs
		pop dx
		mov ds,dx
		or ax,ax		// test the result
		jz badnews
		jmp goodnews
	}
goodnews:
	return TRUE;

badnews:
	errcode = _BL;		// save error code
	installed = FALSE;	// and signal error
	return FALSE;
}

Boolean xmsmgr::fetch (char far* receive, size_t listnum,
	unsigned long bytes, unsigned long ofs)
{
	if (listnum >= 128)	{
		errcode = invhandl;
		installed = FALSE;
		return FALSE;
	}

	xmsparms pfetch;

	if (bytes % 2)
		++bytes;
	pfetch.emb_length = bytes;
	pfetch.dest_handle = 0;
	pfetch.dest_offset = (long)receive;
	pfetch.source_handle = (*list_handles)[listnum][0];
	pfetch.source_offset = ofs;
	long driver = xmsaddr;
	asm	{
		push ds		// be very careful with DS
		push si
		mov ah,0bh	// request EMB MOVE
		lea si,pfetch	// do SI first, about to lost DS
		mov dx,ss
		mov ds,dx
		call dword ptr driver	// driver on stack, so it works
		pop si		// be sure to recover these regs
		pop dx
		mov ds,dx
		or ax,ax	// test the result
		jz badnews
		jmp goodnews
	}
goodnews:
	return TRUE;

badnews:
	errcode = _BL;		// save the error code
	installed = FALSE;	// and signal error
	return FALSE;
}
#endif /* MSDOS */
