/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *	(C) Copyright 1992 by Amit Fridman
 *
 *	Name .....: Virtual.c
 *	Created ..: Friday 13-Nov-92 15:21:53
 *	Revision .: 1
 *
 *	Date        Author                 Comment
 *	=========   ====================   ====================
 *	14-Nov-92   Amit Fridman           Version 1.0 finished
 *	13-Nov-92   Amit Fridman           Created this file!
 *
 * $Revision Header ********************************************************/
#define REVISION	1

#include <exec/memory.h>
#include <libraries/dos.h>
#include <string.h>
#include "Virtual.h"

static char *VMFindBase(struct VirtMem *vm,struct SegEntry *seg,ULONG size,struct SegEntry **postseg,struct SegEntry **preseg);


BOOL InitVirtualMem(struct VirtMem *vm,const char *FName,
								ULONG VirtSize,ULONG PhysSize,USHORT SegNum)
{
	vm->File=Open(FName,MODE_NEWFILE);
	if (vm->File) {
		vm->PhysMem=AllocMem(PhysSize,0);
		if (vm->PhysMem) {
			vm->Segments=AllocMem(SegNum*sizeof(struct SegEntry),MEMF_CLEAR);
			if (vm->Segments) {
				vm->PSegments=NULL;
				vm->VirtSize=VirtSize;
				vm->PhysSize=PhysSize;
				vm->SegNum=SegNum;
				strcpy(vm->FName,FName);
				vm->AccOffset=0;
				vm->Count=0;
				return(TRUE);
			}
		}
		FreeMem(vm->PhysMem,PhysSize);
	}
	Close(vm->File);
	return(FALSE);
}

void FreeVirtMem(struct VirtMem *vm)
{
	FreeMem(vm->Segments,vm->SegNum*sizeof(struct SegEntry));
	FreeMem(vm->PhysMem,vm->PhysSize);
	Close(vm->File);
	DeleteFile(vm->FName);
}

struct SegEntry *CreateSegment(struct VirtMem *vm,ULONG size,SHORT pri)
{
	register int i;
	struct SegEntry *seg;

	if (size>vm->PhysSize) return(NULL);
	for (i=0; i<vm->SegNum; i++)
		if (!(vm->Segments[i].Flags & SF_USED)) break;
	if (i==vm->SegNum) return(NULL);
	seg=&vm->Segments[i];
	seg->NextP=NULL;
	seg->Base=NULL;
	seg->Size=size;
	seg->FOffset=vm->AccOffset;
	seg->Access=0;
	seg->Pri=pri;
	seg->Flags=SF_USED;
	vm->AccOffset+=size;
	Seek(vm->File,seg->FOffset,OFFSET_BEGINNING);
	Write(vm->File,vm->PhysMem,size);
	return(seg);
}

BOOL Access(struct VirtMem *vm,struct SegEntry *seg)
{
	char *Base;
	struct SegEntry *seg1,*seg2;
	ULONG len;

	seg->Access=vm->Count;
	vm->Count++;
	if (seg->Flags & SF_PRESENT) return(TRUE);
	Base=VMFindBase(vm,seg,seg->Size,&seg1,&seg2);
	seg->Base=Base;
	seg->Flags|=SF_PRESENT;
	seg->NextP=seg1;
	if (seg2)
		seg2->NextP=seg;
	else
		vm->PSegments=seg;
	if (!(seg->Flags & SF_INFILE)) return(TRUE);
	Seek(vm->File,seg->FOffset,OFFSET_BEGINNING);
	len=Read(vm->File,seg->Base,seg->Size);
	return(len==seg->Size);
}

static char *VMFindBase(struct VirtMem *vm,struct SegEntry *seg,ULONG size,
									struct SegEntry **postseg,struct SegEntry **preseg)
{
	struct SegEntry *seg1,*seg2;
	char *Base;
	SHORT lowPri;
	ULONG access;
	struct SegEntry *ss1,*ss2;

	while (1) {
		Base=vm->PhysMem;
		seg1=vm->PSegments;
		if (!seg1) {
			*postseg=NULL;
			*preseg=NULL;
			return(Base);
		}
		seg2=NULL;
		while (seg1) {
			if (Base+size<=seg1->Base) { 
				*postseg=seg1;
				*preseg=seg2;
				return(Base);
			}
			Base=seg1->Base+seg1->Size;
			seg2=seg1;
			seg1=seg1->NextP;
		}
		if (Base+size<=vm->PhysMem+vm->PhysSize) {
			*postseg=seg1;
			*preseg=seg2;
			return(Base);
		}
		lowPri=32767;
		access=1<<31;
		for (seg1=vm->PSegments,seg2=NULL; seg1; seg2=seg1,seg1=seg1->NextP)
			if (seg1->Pri<lowPri) {
				lowPri=seg1->Pri;
				access=seg1->Access;
				ss1=seg1;
				ss2=seg2;
			} else
				if ((seg1->Pri==lowPri) && (seg1->Access<access)) {
					access=seg1->Access;
					ss1=seg1;
					ss2=seg2;
				}
		if (ss2)
			ss2->NextP=ss1->NextP;
		else
			vm->PSegments=ss1->NextP;
		if (ss1->Flags & SF_DIRTY) {
			Seek(vm->File,ss1->FOffset,OFFSET_BEGINNING);
			Write(vm->File,ss1->Base,ss1->Size);
			ss1->Flags^=SF_DIRTY;
		}
		ss1->Flags^=SF_PRESENT;
		ss1->Flags|=SF_INFILE;
	}
	return(NULL);
}
