#include "os.h"

/*
 * Note: MS-DOS terminology is different.  When they say "logical page", most
 * people would say "physical page".  When they say "physical page", most
 * people would say "virtual page".
 *
 * In this code I assume there are 4 physical (virtual) pages.
 */

_segment _near ems_base = 0;

/* structure for each of the 4 "physical" pages */
struct ems_phys _near phys[NPHYS];
static u_short generation = 0;
static void _far ems_shutdown(void);

int
ems_init()
{
	int fd;
	unsigned char retcode;
	u_char version;

	fd = open("emmxxxx0", 1);
	if (fd < 0) {
		return -1;
	}
	close(fd);
	{
		_asm	mov	ah,0x41
		_asm	int	0x67
		_asm	mov	ems_base,bx
		_asm	mov	retcode,ah
	}
	if (retcode)
		goto out;
printf("ems_base=%x\n", ems_base);
	{
		_asm	mov	ah,0x46
		_asm	int	0x67
		_asm	mov	retcode,ah
		_asm	mov	version,al
	}
printf("EMS version %d.%d\n", version >> 4, version & 0xf);
	atexit(ems_shutdown);
	miRegionEMSInit();
out:
printf("ems_init returns %x\n", retcode);
	return retcode;
}

/* Deallocate pages */
int
ems_shut(handle)
u_short handle;
{
	u_char retcode;

	{
		_asm	mov	ah,0x45
		_asm	mov	dx,handle
		_asm	int	0x67
		_asm	mov	retcode,ah
	}
printf("ems_shut(%x) returns %x\n", handle, retcode);
	return retcode;
}

static void
ems_shutdown()
{
	/* call everything that will release pages */
	miRegionEMSShut();
}

/* Allocate n pages to a handle. */
int
ems_alloc(n)
int n;
{
	int handle;
	u_char retcode;
	u_char _near *cp;

	{
		_asm	mov	ah,0x43
		_asm	mov	bx,n
		_asm	int	0x67
		_asm	mov	handle,dx
		_asm	mov	retcode,ah
	}
	if (retcode) {
		printf("retcode %x\n", retcode);
		return -1;	/* I hope this is not a valid handle. */
	}
printf("ems_alloc returns %x\n", handle);
	return handle;
}

/* Change number of pages a handle has.  If something fails, this returns
 * non-zero.  The calling function should probably shut down the server
 * at this point.
 */
int
ems_realloc(handle, n)
u_short handle;
int n;
{
	u_char retcode;

	{
	    _asm	mov	ah,0x51
	    _asm	mov	dx,handle
	    _asm	mov	bx,n
	    _asm	int	0x67
	    _asm	mov	n,bx
	    _asm	mov	retcode,ah
	}
printf("ems_realloc(%x,%d) returns %x\n", handle, n, retcode);
	return retcode;
}

/* Map page #page of handle and return the segment that can be used
 * for this physical page. */
_segment
ems_map(handle, page)
u_short handle;
int page;
{
	u_short gen;
	struct ems_phys _near *pp, _near *lp = 0;
	u_char phys_page, retcode;

	generation++;
	gen = generation;
	/* This loop does two things:  It finds the oldest page (the one
	 * with the lowest gen) and also checks to see if this page is
	 * already mapped. */
	for (pp = phys; pp < &phys[NPHYS]; pp++) {
		if (pp->handle == handle && pp->page == page) {
			phys_page = (u_char)(pp - phys);
			goto out;
		}
		if (pp->flags & EMS_LOCKED)
			continue;
		if (!lp || pp->gen < gen) {
			lp = pp;
			gen = lp->gen;
		}
	}
	if (!lp) {
printf("oh oh: ems_map fails\n");
		return 0;
	}
	phys_page = lp - phys;
printf("map %x:%x to %d\n", handle, page, phys_page);
	{
	    _asm	mov	ah,0x44
	    _asm	mov	al,phys_page
	    _asm	mov	dx,handle
	    _asm	mov	bx,page
	    _asm	int	0x67
	    _asm	mov	retcode,ah
	}
	if (retcode) {
printf("ems_map: retcode %x\n", retcode);
		return 0;	/* problem */
	}
	lp->gen = generation;
	lp->handle = handle;
	lp->page = page;
out:
	return ems_base + (u_short)phys_page * 0x400;
}

void _fastcall
ems_lock(seg)
_segment seg;
{
	phys[(u_short)(seg - ems_base) / 0x400 ].flags |= EMS_LOCKED;
}

void _fastcall
ems_unlock(seg)
_segment seg;
{
	phys[(u_short)(seg - ems_base) / 0x400].flags &= ~EMS_LOCKED;
}
