#include	<stdio.h>
#include	<conio.h>
#include	<malloc.h>
#include	<dos.h>
#include	"emm.h"
#define		MEMORY
#include	"vmemory.h"
#undef		MEMORY


int	vmsearch(unsigned long address)
{
	unsigned int	c,free1;
	unsigned long	adds;

	adds = address & VMADDMASK;

		/* if already mapped */
	if(vmismap(address))
	{
		vmm.address = address;
		return(1);
	}

	free1 = 0xffff;
	for(c = 0;c < vmtblnum;c++)
	{
		if((vmtable[c].use == VMFREE) || (vmtable[c].use == VMBLANK))
		{
			if(c < free1)
				free1 = c;
			continue;
		}

		if(adds != vmtable[c].address)
			continue;

		if(vmtable[c].type == VMEMS)
		{
			if(vmentry != c)
			{
				if(!emsmapf(vmtable[c].page,0))
					return(0);
			}
		}
		vmentry = c;
		vmm.addbase = adds;
		vmm.address = address;

		break;
	}
	vmm.freeent = free1;

	if(c >= vmtblnum)
		return(0);

	return(1);
}


int	vmmap(unsigned long address)
{
	unsigned int	c;
	unsigned long	adds;
	unsigned char	far *addp;

	if(vmsearch(address))
		return(1);

	adds = address & VMADDMASK;

		/* if no free entry */
	if(vmm.freeent == 0xffff)
	{
		if(!vmm.emsready)	/* ems not available */
			return(0);

		if(vmtblnum >= VMTBLSIZE)	/* vmtable not available */
			return(0);

		if(!emsrealc(emm.lpages + 1))	/* ems can not allocate */
			return(0);

		vmtable[vmtblnum].type = VMEMS;
		vmtable[vmtblnum].page = emm.lpages - 1;
		vmtable[vmm.freeent].use = VMFREE;

		vmm.freeent = vmtblnum++;
	}

	if(vmtable[vmm.freeent].type == VMCONV)
		addp = vmcmtbl[vmm.freeent];
	else	/* EMS */
	{
		if(!emsmapf(vmtable[vmm.freeent].page,0))
			return(0);
		addp = vmm.emsadd;
	}
	if(vmtable[vmm.freeent].use != VMBLANK)
	{
		for(c = 0;c < VMMEMSIZE;c++)
			*addp++ = 0xff;
	}

	vmtable[vmm.freeent].address = adds;
	vmtable[vmm.freeent].use = VMINUSE;

	vmentry = vmm.freeent;
	vmm.addbase = adds;
	vmm.address = address;

	return(1);
}


unsigned char	far *vmmap1(unsigned long address)
{
	unsigned int	c;
	unsigned long	adds;
	unsigned char	far *ptr;

	adds = address & VMADDMASK;

	for(c = 0;c < vmtblnum;c++)
	{
		if((vmtable[c].use == VMFREE) || (vmtable[c].use == VMBLANK))
			continue;

		if(adds != vmtable[c].address)
			continue;

		if(vmtable[c].type == VMCONV)
			ptr = vmcmtbl[c] + (address & ~VMADDMASK);
		else	/* EMS */
		{
			if(!emsmapf(vmtable[c].page,1))
				return((unsigned char far *)0l);
			FP_SEG(ptr) = emm.ppflame[1].segment;
			FP_OFF(ptr) = (unsigned int)(address & ~VMADDMASK);
		}

		break;
	}

	if(c >= vmtblnum)
		return((unsigned char far *)0l);

	return(ptr);
}


int	vmgarbag()
{
	int	freecnt;
	unsigned int	entry,c;
	unsigned char	far *addp;

	freecnt = 0;

	for(entry = 0;entry < vmtblnum;entry++)
	{
		if(vmtable[entry].type == VMCONV)
			addp = vmcmtbl[entry];
		else	/* EMS */
		{
			if(!emsmapf(vmtable[entry].page,0))
				return(0);
			addp = vmm.emsadd;
		}

		for(c = 0;c < VMMEMSIZE;c++)
		{
			if(0xff != *addp++)
				break;
		}
		if(c == VMMEMSIZE)	/* blank (all data is 0xff) */
		{
			vmtable[entry].use = VMBLANK;
			freecnt++;
		}
	}

	return(freecnt);
}


int	vminit()
{
	int	c;
	unsigned int	freesize;

	vmm.vmready = 0;
	vmm.emsready = 0;

		/* get virtual memory table */
	if(NULL == (vmtable = (VMTABLE *)calloc(VMTBLSIZE,sizeof(VMTABLE))))
		return(0);

		/* get conventional memory block */
	freesize = _memavl();
	if(freesize > 0x100)
		freesize -= 0x100;

	for(c = 0;c < VMCMBSIZE ;c++)
	{
		if(NULL == (vmcmtbl[c] = _fmalloc(VMMEMSIZE)))
			break;
		if(freesize > _memavl())
		{
			_ffree(vmcmtbl[c]);
			break;
		}
		vmtable[c].type = VMCONV;
		vmtable[c].use = VMFREE;
	}
	vmtblnum = c;

	if(c)
		vmm.vmready = 1;
	else
		vmm.vmready = 0;

		/* get EMS handle */
	vmm.emsready = 0;
	if(emsphys())
	{
		if(emsalloc(0))
		{
			FP_SEG(vmm.emsadd) = emm.ppflame[0].segment;
			FP_OFF(vmm.emsadd) = 0;

			vmm.emsready = 1;
		}
	}

	if(!vmm.vmready && !vmm.emsready)	/* no memory */
		return(0);

	vmentry = 0;
	vmm.address = 0l;
	vmm.step = 1l;

	return(1);
}


int	vmflush()
{
	int	c;

	if(vmm.emsready)
		emsfree();

	if(!vmm.vmready)
		return(1);

	for(c = 0;c < vmtblnum;c++)
	{
		if(vmtable[c].type == VMCONV)
			_ffree(vmcmtbl[c]);
	}
	free((char *)vmtable);

	vmm.vmready = 0;
	vmm.emsready = 0;

	return(1);
}


void	vmaddr(unsigned long address)
{
	vmm.address = address;

	return;
}

void	vmstep(long step)
{
	vmm.step = step;

	return;
}


unsigned char	vmread()
{
	unsigned char	far *vmptr;

		/* if logical address is out of phisical memory block */
	if(!vmismap(vmm.address))
	{
		if(!vmsearch(vmm.address))	/* if no entry */
		{
			vmm.address += vmm.step;
			return(0xff);	/* blank data is 0xff */
		}
	}

	setvmptr(vmptr);
	vmm.address += vmm.step;

	return(*vmptr);
}


int	vmwrite(unsigned char data)
{
	unsigned char	far *vmptr;

		/* if logical address is out of phisical memory block */
	if(!vmismap(vmm.address))
	{
		if(!vmmap(vmm.address))	/* if ems memory not available */
		{
			if(!vmgarbag())		/* if no entry that all data is 0xff */
				return(0);

			if(!vmmap(vmm.address))	/* if can not mapping free entry */
				return(0);
		}
	}

	setvmptr(vmptr);
	*vmptr = data;
	vmm.address += vmm.step;

	return(1);
}


int	vmreadb(unsigned char *buf,unsigned int size)
{
	long	offset;
	unsigned char	far *vmptr;

	for(offset = 0xffffffff;size;size--)
	{
		if((offset < 0l) || (offset >= VMMEMSIZE))
		{
			if(vmsearch(vmm.address))
			{
				setvmptr(vmptr);
				offset = vmm.address & ~VMADDMASK;
			}
			else		/* if no entry */
				vmptr = (unsigned char far *)0l;
		}

		if((unsigned char far *)0l == vmptr)
			*buf++ = 0xff;		/* blank data is 0xff */
		else
		{
			*buf++ = *vmptr;
			vmptr += (int)vmm.step;
		}

		offset += vmm.step;
		vmm.address += vmm.step;
	}

	return(1);
}


int	vmwriteb(unsigned char *buf,unsigned int size)
{
	long	offset;
	unsigned char	far *vmptr;

	for(offset = 0xffffffff;size;size--)
	{
		if((offset < 0l) || (offset >= VMMEMSIZE))
		{
			if(!vmmap(vmm.address))	/* if ems memory not available */
			{
				if(!vmgarbag())		/* if no entry that all data is 0xff */
					return(0);

				if(!vmmap(vmm.address))	/* if can not mapping free entry */
					return(0);
			}
			setvmptr(vmptr);
			offset = vmm.address & ~VMADDMASK;
		}
		*vmptr = *buf++;

		vmptr += (int)vmm.step;
		offset += vmm.step;
		vmm.address += vmm.step;
	}

	return(1);
}


int	vmmove(unsigned long sorce,unsigned long distin,unsigned long size)
{
	unsigned long	soffset,doffset;
	unsigned char	far *svmptr,far *dvmptr;
	int	direc;

	if(size == 0l)
		return(1);

	if(sorce == distin)
		return(1);
	else if(sorce > distin)
		direc = 1;
	else if((sorce + size - 1l) < distin)
		direc = 1;
	else
	{
		sorce += (size - 1l);
		distin += (size - 1l);
		direc = 0;
	}

	for(soffset = doffset = 0xffffffff;size;size--)
	{
		if(soffset >= VMMEMSIZE)
		{
			svmptr = vmmap1(sorce);
			soffset = sorce & ~VMADDMASK;
		}

		if(doffset >= VMMEMSIZE)
		{
			if(!vmmap(distin))	/* if ems memory not available */
			{
				if(!vmgarbag())		/* if no entry that all data is 0xff */
					return(0);

				if(!vmmap(distin))	/* if can not mapping free entry */
					return(0);
			}
			setvmptr(dvmptr);
			doffset = distin & ~VMADDMASK;
		}

		if(direc == 1)
		{
			if((unsigned char far *)0l == svmptr)
				*dvmptr++ = 0xff;	/* blank memory */
			else
				*dvmptr++ = *svmptr++;

			soffset++;
			doffset++;
			sorce++;
			distin++;
		}
		else
		{
			if((unsigned char far *)0l == svmptr)
				*dvmptr-- = 0xff;	/* blank memory */
			else
				*dvmptr-- = *svmptr--;

			soffset--;
			doffset--;
			sorce--;
			distin--;
		}
	}

	return(1);
}


int	vmfill(unsigned long address,unsigned char data,unsigned long size)
{
	unsigned long	offset;
	unsigned char	far *vmptr;

	for(offset = 0xffffffff;size;size--)
	{
		if(offset >= VMMEMSIZE)
		{
			if(!vmmap(address))	/* if ems memory not available */
			{
				if(!vmgarbag())		/* if no entry that all data is 0xff */
					return(0);

				if(!vmmap(address))	/* if can not mapping free entry */
					return(0);
			}
			setvmptr(vmptr);
			offset = address & ~VMADDMASK;
		}

		*vmptr++ = data;

		offset++;
		address++;
	}

	return(1);
}


int	vmerase()
{
	unsigned int	c;

	for(c = 0;c < vmtblnum;c++)
		vmtable[c].use = VMFREE;

	for(c = 0;c < vmtblnum;c++)
	{
		if(vmtable[c].type != VMCONV)
			break;
	}
	if(vmm.emsready)	/* ems available */
	{
		if(emsrealc(0))		/* free ems logical pages */
			vmtblnum = c;
	}
	else
		vmtblnum = c;

	vmm.address = 0l;
	vmm.step = 1l;

	return(1);
}


unsigned long	vmfirst(unsigned long start,unsigned long end)
{
	unsigned char	far *addp;
	unsigned long	addrf,addr;
	unsigned int	entry,c;

	if(start > end)
		return(0l);

	addrf = end;

	for(entry = 0;entry < vmtblnum;entry++)
	{
		if((vmtable[entry].use == VMFREE) || (vmtable[entry].use == VMBLANK))
			continue;

		if(vmtable[entry].type == VMCONV)
			addp = vmcmtbl[entry];
		else	/* EMS */
		{
			if(!emsmapf(vmtable[entry].page,0))
				return(0l);
			addp = vmm.emsadd;
		}
		addr = vmtable[entry].address;
		if((addr < (start & VMADDMASK)) || (addr > (end & VMADDMASK)))
			continue;

		for(c = 0;c < VMMEMSIZE;c++)
		{
			if(0xff != *addp++)
			{
				if((start <= addr) && (addr <= end))
				{
					if(addrf > addr)
						addrf = addr;
				}
			}
			addr++;
		}
	}

	return(addrf);
}


unsigned long	vmlast(unsigned long start,unsigned long end)
{
	unsigned char	far *addp;
	unsigned long	addrl,addr;
	unsigned int	entry,c;

	if(start > end)
		return(0l);

	addrl = start;

	for(entry = 0;entry < vmtblnum;entry++)
	{
		if((vmtable[entry].use == VMFREE) || (vmtable[entry].use == VMBLANK))
			continue;

		if(vmtable[entry].type == VMCONV)
			addp = vmcmtbl[entry];
		else	/* EMS */
		{
			if(!emsmapf(vmtable[entry].page,0))
				return(0);
			addp = vmm.emsadd;
		}
		addr = vmtable[entry].address;
		if((addr < (start & VMADDMASK)) || (addr > (end & VMADDMASK)))
			continue;

		for(c = 0;c < VMMEMSIZE;c++)
		{
			if(0xff != *addp++)
			{
				if((start <= addr) && (addr <= end))
				{
					if(addrl < addr)
						addrl = addr;
				}
			}
			addr++;
		}
	}

	return(addrl);
}
