#include <stdlib.h>
#include <exec/memory.h>
#if defined __SASC
#include <proto/exec.h>
#include <proto/dos.h>
#else
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#endif

static char *Ver =
		"$VER:MemTest 1.1"
#if defined __SASC
		" "	__AMIGADATE__ " ©1994 Leopold-Soft, leopold@cs.tut.fi"
#endif
		;

struct MemNode {
	struct MemNode *NextNode;
	long Bytes;
};

struct MemTypeStruct {
	char *Text;
	unsigned long Value;
};

static struct MemTypeStruct MemType[] = {
	{"24BITDMA", 1L<<9},
	{"CHIP", 1L<<1},
	{"FAST", 1L<<2},
	{"PUBLIC", 1L<<0},
	{"LOCAL", 1L<<8},
	{NULL, 0}
};

int main(void) {
	static struct MemNode *FirstNode = NULL, *CurrNode = NULL, *LastNode, *NextNode;
	static short OK, tmp;
	static long BufSize = 64*1024, i, Errors = 0, Bytes = 0, OriginalPri, MyType;
	volatile unsigned long *CheckLong;	/* If these weren't "volatile", the compiler	*/
	volatile unsigned char *CheckByte;	/*	would optimize all tests away!				*/
	static struct Process *MyProcess;
	static BPTR OrigOutput, NewOutput = NULL;
	static unsigned char Test00, TestFF;
	static unsigned long Test00000000, TestFFFFFFFF;

	MyProcess = (struct Process *) FindTask(0);
	OriginalPri = SetTaskPri((struct Task *) MyProcess, 5);

	OrigOutput = Output();
	if (!OrigOutput) {
		NewOutput = Open("CON:10/10/600/300/MemTest ©1994 Leopold-Soft, leopold@cs.tut.fi",
						MODE_NEWFILE);
		if (NewOutput) {
			SelectOutput(NewOutput);
		}
	}

	Printf("\nMemory test v1.1 ©1994 Leopold-Soft, leopold@cs.tut.fi\n"
			"Henrik Herranen, Atomikatu 6A9, SF-33720 Tampere, Finland\n");

	do {
		do {
			if (!FirstNode) {
				FirstNode = AllocMem(BufSize, MEMF_ANY);
				if (FirstNode) {
					Bytes += BufSize;
					CurrNode = FirstNode;
					CurrNode->NextNode = NULL;
					CurrNode->Bytes = BufSize;
					OK = 1;
				} else {
					BufSize /= 2;
					OK = 0;
				}
			} else {
				NextNode = AllocMem(BufSize, MEMF_ANY);
				if (NextNode) {
					Bytes += BufSize;
					CurrNode->NextNode = NextNode;
					CurrNode = NextNode;
					CurrNode->NextNode = NULL;
					CurrNode->Bytes = BufSize;
					OK = 1;
				} else {
					BufSize /= 2;
					OK = 0;
				}
			}
		} while(!OK && BufSize >= sizeof(struct MemNode));

		if (OK) {
			MyType = TypeOfMem(CurrNode);
			Printf("Allocated ");
			if (BufSize >= 1024) Printf("%3ld KB", BufSize/1024);
			else Printf("%4ld B", BufSize);
			Printf(" at $%8lX, type ", CurrNode);

			i = 0;
			tmp = 0;
			do {
				if (MyType & MemType[i].Value) {
					if (tmp) Printf(" + ");
					tmp = 1;
					Printf("%s", MemType[i].Text);
				}
			} while (MemType[++i].Text);
			if (!tmp) Printf("UNKNOWN!");
			Printf("\n");

			CheckLong = (unsigned long *) ((unsigned char *) CurrNode + sizeof(struct MemNode));

			i = (BufSize - sizeof(struct MemNode)) / sizeof(unsigned long);
			while (i--) {
				*CheckLong = 0x00000000;
				Test00000000 = *CheckLong;
				*CheckLong = 0xFFFFFFFF;
				TestFFFFFFFF = *CheckLong;
				if (Test00000000 || TestFFFFFFFF!=0xFFFFFFFF) {
					long j = sizeof(unsigned long);
					CheckByte = (unsigned char *) CheckLong;
					while (j--) {
						*CheckByte = 0x00;
						Test00 = *CheckByte;
						*CheckByte = 0xFF;
						TestFF = *CheckByte;
						if (Test00 || TestFF!=0xFF) {
							Printf("ERROR: @$%lX, $00->$%02lX, $ff->$%02lX\n",
								CheckByte, (unsigned long) Test00, (unsigned long) TestFF);
							Errors++;
						}
						CheckByte++;
					}
				}
				CheckLong++;
			}
		}
	} while(OK);

	Printf("\nCheck completed, ");
	if (!Errors) {
		Printf("no errors found in %ld KB (lucky you)!\n\n", Bytes/1024);
	} else {
		Printf("%ld errors found in %ld KB (too bad)!\n\n", Errors, Bytes/1024);
	}

	Printf("Freeing resources...\n");
	CurrNode = FirstNode;
	while (CurrNode) {
		LastNode = CurrNode;
		CurrNode = CurrNode->NextNode;
		FreeMem(LastNode, LastNode->Bytes);
	}
	Printf("  ...done!\n\n");

	SetTaskPri((struct Task *) MyProcess, OriginalPri);

	if (NewOutput) {
		Delay(200);
		SelectOutput(NULL);
		Close(NewOutput);
	}

	return (Errors) ? 30 : 0;
}
