/*
**	Memory.c
**
**	Memory management routines
**
**	Copyright © 1990-1996 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#ifndef _GLOBAL_H
#include "Global.h"
#endif

	// Main memory pool puddle size

#define PUDDLE_SIZE 32768

	// Memory allocation data

STATIC struct SignalSemaphore	MemorySemaphore;
STATIC APTR			MemoryPool;

	// Diagnostic data

typedef char (* __asm SegTrack(REG(a0) ULONG Address,REG(a1) ULONG *SegNum,REG(a2) ULONG *Offset));

struct SegSem
{
	struct SignalSemaphore	 seg_Semaphore;
	SegTrack		*seg_Find;
};

struct FirewallInfo
{
	ULONG			Checksum;

	ULONG			Address;
	ULONG			TotalSize;
	ULONG			ReturnAddress;
	ULONG			Size;
	ULONG			FillByte;

	ULONG			FirePre;
	ULONG			FirePost;
};

STATIC LONG			FirewallPre = 0,FirewallPost = 0;
STATIC ULONG			FillByte = 1;

STATIC LONG			Smallest = -1,Largest = -1;

	/* MemorySetup():
	 *
	 *	Set up the main memory pool.
	 */

BOOL
MemorySetup()
{
	BOOL DosOpen = FALSE;

	if(!DOSBase)
	{
		if(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",37))
			DosOpen = TRUE;
	}

	if(DOSBase)
	{
		UBYTE LocalBuffer[256];

		if(GetVar("TERMPREALLOC",LocalBuffer,256,NULL) > 0)
		{
			if(StrToLong(LocalBuffer,&FirewallPre) > 0)
			{
				if(FirewallPre < 16)
					FirewallPre = 16;

				FirewallPost = 16;

				if(GetVar("TERMPOSTALLOC",LocalBuffer,256,NULL) > 0)
				{
					if(StrToLong(LocalBuffer,&FirewallPre) <= 0)
						FirewallPost = 16;
				}

				FirewallPre	= (FirewallPre + 15) & ~15;
				FirewallPost	= (FirewallPost + 15) & ~15;

				if(!FirewallPost)
					FirewallPost = 16;
			}
			else
				FirewallPost = 0;
		}

		if(DosOpen)
		{
			CloseLibrary((struct Library *)DOSBase);
			DOSBase = NULL;
		}
	}

	InitSemaphore(&MemorySemaphore);

	if(MemoryPool = AsmCreatePool(MEMF_PUBLIC | MEMF_ANY,PUDDLE_SIZE,PUDDLE_SIZE,SysBase))
		return(TRUE);
	else
		return(FALSE);
}

	/* MemoryCleanup():
	 *
	 *	Clean up the main memory pool.
	 */

VOID
MemoryCleanup()
{
	if(Smallest != -1)
	{
		DB(kprintf("term stats: smallest allocation was %ld bytes\n",Smallest));
		Smallest = -1;
	}

	if(Largest != -1)
	{
		DB(kprintf("term stats: largest allocation was %ld bytes\n",Largest));
		Largest = -1;
	}

	if(MemoryPool)
	{
		AsmDeletePool(MemoryPool,SysBase);

		MemoryPool = NULL;
	}
}

	/* FreeVecPooled(APTR Memory):
	 *
	 *	Free a memory chunk.
	 */

VOID __asm
FreeVecPooledLocal(REG(a0) APTR Memory,REG(a1) ULONG ReturnAddress)
{
	if(Memory && MemoryPool)
	{
		ULONG *Data = (ULONG *)Memory;

		ObtainSemaphore(&MemorySemaphore);

		if(FirewallPre)
		{
			struct FirewallInfo *Info;
			ULONG Sum,*Thing;
			LONG i;

			Info = (struct FirewallInfo *)(((ULONG)Memory) - (sizeof(struct FirewallInfo) + FirewallPre));

			for(Sum = 0, i = 1, Thing = &Info -> Address ; i < sizeof(struct FirewallInfo) / sizeof(ULONG) ; i++)
				Sum += Thing[i];

			if(Info -> Checksum == ~Sum && Memory == (APTR)Info -> Address)
			{
				UBYTE *Data;
				LONG i,TotalSize;
				BOOL AllocationProblem = FALSE;

				Data = (UBYTE *)Info -> FirePre;

				for(i = 0 ; i < FirewallPre ; i++)
				{
					if(Data[i] != Info -> FillByte)
					{
						LONG j;

						AllocationProblem = TRUE;

						DB(kprintf("term: Allocation @ 0x%08lx, Size %ld got stomped on, %ld bytes trashed before, FillByte %02lx:",Memory,Info -> Size,FirewallPre - i,Info -> FillByte));

						Data = &Data[i];

						for(j = 0 ; j < FirewallPre - i ; j++)
						{
							if(!(j & 7))
								DB(kprintf("\n      %08lx: ",&Data[j]));

							DB(kprintf("%02lx ",Data[j]));
						}

						DB(kprintf("\n"));

						break;
					}
				}

				Data = (UBYTE *)Info -> FirePost;

				for(i = FirewallPost - 1 ; i >= 0 ; i--)
				{
					if(Data[i] != Info -> FillByte)
					{
						LONG j;

						Data = (UBYTE *)Info -> FirePost;

						DB(if(AllocationProblem))
							DB(kprintf("      %ld bytes trashed after:",i + 1));
						DB(else)
							DB(kprintf("term: Allocation @ 0x%08lx, Size %ld got stomped on, %ld bytes trashed after, FillByte 0x%02lx:",Memory,Info -> Size,i + 1,Info -> FillByte));

						AllocationProblem = TRUE;

						for(j = 0 ; j <= i ; j++)
						{
							if(!(j & 7))
								DB(kprintf("\n      %08lx: ",&Data[j]));

							DB(kprintf("%02lx ",Data[j]));
						}

						DB(kprintf("\n"));

						break;
					}
				}

				if(AllocationProblem)
				{
					struct SegSem *SegTracker;

					Forbid();

					if(SegTracker = (struct SegSem *)FindSemaphore("SegTracker"))
					{
						char LocalBuffer[256];
						ULONG Segment,Offset;
						char *Name;

						DB(kprintf("\n"));

						if(Name = (*SegTracker -> seg_Find)(Info -> ReturnAddress,&Segment,&Offset))
						{
							CopyMem(Name,LocalBuffer,255);
							LocalBuffer[255] = 0;

							DB(kprintf("      Alloc by |%s| %lx:%lx\n",LocalBuffer,Segment,Offset));
						}

						if(Name = (*SegTracker -> seg_Find)(ReturnAddress,&Segment,&Offset))
						{
							CopyMem(Name,LocalBuffer,255);
							LocalBuffer[255] = 0;

							DB(kprintf("       Free by |%s| %lx:%lx\n",LocalBuffer,Segment,Offset));
						}
					}

					Permit();
				}

					// Let's misbehave...

				TotalSize = Info -> TotalSize;

				memset(Info,0x11,TotalSize);

				AsmFreePooled(MemoryPool,Info,TotalSize,SysBase);
			}
			else
			{
				struct SegSem *SegTracker;

				Forbid();

				if(SegTracker = (struct SegSem *)FindSemaphore("SegTracker"))
				{
					char LocalBuffer[256];
					ULONG Segment,Offset;
					char *Name;

					if(Name = (*SegTracker -> seg_Find)(ReturnAddress,&Segment,&Offset))
					{
						CopyMem(Name,LocalBuffer,255);
						LocalBuffer[255] = 0;

						DB(kprintf("       Bogus free from 0x%08lx by |%s| %lx:%lx\n",Memory,LocalBuffer,Segment,Offset));
					}
				}

				Permit();
			}
		}
		else
			AsmFreePooled(MemoryPool,&Data[-1],Data[-1],SysBase);

		ReleaseSemaphore(&MemorySemaphore);
	}
}

	/* AllocVecPooled(ULONG Size,ULONG Flags):
	 *
	 *	Allocate a memory chunk from the main memory pool and
	 *	remember its size.
	 */

APTR __asm
AllocVecPooledLocal(REG(d0) ULONG Size,REG(d1) ULONG Flags,REG(a0) ULONG ReturnAddr)
{
	if(MemoryPool)
	{
		ObtainSemaphore(&MemorySemaphore);

		if(FirewallPre)
		{
			struct FirewallInfo *Info;
			ULONG FireSize;
			ULONG RoundSize = (Size + 3) & ~3;

			if(Smallest == -1)
				Smallest = Size;

			if(Largest == -1)
				Largest = Size;

			if(Size < Smallest)
				Smallest = Size;

			if(Size > Largest)
				Largest = Size;

			FireSize = sizeof(struct FirewallInfo) + FirewallPre + RoundSize + FirewallPost;

			if(Info = (struct FirewallInfo *)AsmAllocPooled(MemoryPool,FireSize,SysBase))
			{
				ULONG *Data,Sum,*Thing;
				LONG i;

				Info -> Address		= ((ULONG)Info) + sizeof(struct FirewallInfo) + FirewallPre;
				Info -> TotalSize	= FireSize;
				Info -> ReturnAddress	= ReturnAddr;
				Info -> Size		= Size;
				Info -> FillByte	= FillByte;

				Info -> FirePre		= (ULONG)&Info[1];
				Info -> FirePost	= Info -> FirePre + FirewallPre + RoundSize;

				memset((APTR)(Info -> FirePre),FillByte,FirewallPre);
				memset((APTR)(Info -> FirePost),FillByte,FirewallPost);

				for(Sum = 0, i = 1, Thing = &Info -> Address ; i < sizeof(struct FirewallInfo) / sizeof(ULONG) ; i++)
					Sum += Thing[i];

				Info -> Checksum = ~Sum;

				FillByte = (FillByte + 2) & 0xFF;

				Data = (ULONG *)Info -> Address;

				ReleaseSemaphore(&MemorySemaphore);

				if(Flags & MEMF_CLEAR)
				{
					register ULONG *Memory = Data;

					Size = RoundSize / sizeof(ULONG) - 1;

					do
						*Memory++ = 0;
					while(Size--);
				}
				else
				{
					register ULONG *Memory = Data;

					Size = RoundSize / sizeof(ULONG) - 1;

					do
						*Memory++ = 0xDEADBEEF;
					while(Size--);
				}

				return((APTR)Data);
			}
		}
		else
		{
			ULONG *Data;

			Size = (Size + sizeof(ULONG) + 3) & ~3;

			if(Data = (ULONG *)AsmAllocPooled(MemoryPool,Size,SysBase))
			{
				ReleaseSemaphore(&MemorySemaphore);

				*Data++ = Size;

					// Zero the chunk if necessary

				if(Flags & MEMF_CLEAR)
				{
					register ULONG *Memory = Data;

					Size = Size / sizeof(ULONG) - 2;

					do
						*Memory++ = 0;
					while(Size--);
				}

				return((APTR)Data);
			}
		}

		ReleaseSemaphore(&MemorySemaphore);
	}

	return(NULL);
}
