/*
 * Program: CheckMem
 * Author: Osma Ahvenlampi
 * Description: Check memory
 *
 */

#include <exec/memory.h>
#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/execbase.h>
#include <dos/rdargs.h>

#include <proto/exec.h>
#include <proto/dos.h>

#include <stdlib.h>
#include <stdio.h>
#include <time.h>

#define TEMPLATE "FROM,TO,TIMES/N/K,FORCE/S,QUIET/S"
#define ARG_FROM 0
#define ARG_TO 1
#define ARG_TIMES 2
#define ARG_FORCE 3
#define ARG_QUIET 4
#define ARGNUM 5

#define VERSION "1.1"

extern struct ExecBase *SysBase;

LONG ArgArray[ARGNUM];
BOOL quiet;

const char VersionID[] = "$VER: CheckMem " VERSION "(11.12.94)";

ULONG TestVals[] = { 0x00000000, 0xffffffff, 0xaaaaaaaa, 0x55555555 };

ULONG __asm CheckMemBlock( register __a0 UBYTE * );

void PrintList( struct MemHeader * );

int CheckMemRegion( UBYTE *, UBYTE * );

void __regargs __chkabort(void) {}

void 
main(char *arg, int argv)
{
	struct RDArgs *RDArgs;
	UBYTE *low, *high;
	struct MemHeader *l;
	int error = 0, i;
	time_t t;

	SetSignal( 0L, SIGBREAKF_CTRL_C );

	if (RDArgs = ReadArgs(TEMPLATE, ArgArray, NULL))
	{
		LONG oldpri;

		(void)time(&t);
		
		printf("CheckMem " VERSION " by Osma Ahvenlampi\n"
			"Memory check started on %s\n", ctime(&t));
		
		i = ArgArray[ARG_TIMES] ? *(int *)ArgArray[ARG_TIMES] : 1;
		quiet = (BOOL)ArgArray[ARG_QUIET];

		if ((oldpri = SetTaskPri(FindTask(NULL),-1)) < -1)
			SetTaskPri(FindTask(NULL),oldpri);
		
		if (ArgArray[ARG_FROM] && ArgArray[ARG_TO])
		{
			BOOL within = FALSE;
			low = (UBYTE *) strtoul((char *)ArgArray[ARG_FROM], NULL, 16);
			high = (UBYTE *) strtoul((char *)ArgArray[ARG_TO], NULL, 16);

			for ( l = (struct MemHeader *)SysBase->MemList.lh_Head ;
					 l->mh_Node.ln_Succ ; l=(struct MemHeader *)l->mh_Node.ln_Succ )
			{
				if (low >= (UBYTE *)l && high <= (UBYTE *)l->mh_Upper)
					within = TRUE;
			}
			
			if (!within)
			{
				if (!ArgArray[ARG_FORCE])
				{
					printf("Addresses outside Exec MemList. Use FORCE keyword if you really mean it.\n");
					exit(20);
				}
				if (FindTask("« Enforcer »"))
				{
					printf("Can't check addresses outside Exec MemList with Enforcer on.\n");
					exit(20);
				}
			}
			while ( i-- )
			{
				if (!quiet)
					printf("Checking memory from 0x%x to 0x%x...\n", low, high);
				error |= CheckMemRegion( low, high );
				if (SetSignal( 0L, 0L ) & SIGBREAKF_CTRL_C)
					goto ctrlc;
			}
		}
		else
		{
			while ( i-- )
			{
				for ( l = (struct MemHeader *)SysBase->MemList.lh_Head ;
					 l->mh_Node.ln_Succ ; l=(struct MemHeader *)l->mh_Node.ln_Succ )
				{
					if (!quiet)
						printf("Checking %s from 0x%x to 0x%x...\n", l->mh_Node.ln_Name, l, (UBYTE *)l->mh_Upper-1);
					error |= CheckMemRegion( (UBYTE *)l, (UBYTE *)l->mh_Upper-1 );
					if (SetSignal( 0L, 0L ) & SIGBREAKF_CTRL_C)
						goto ctrlc;
				}
			}
		}
ctrlc:
		FreeArgs(RDArgs);
	}
	else
		PrintFault(IoErr(),"CheckMem");
	if (error)
		printf("Errors detected.\n");
	exit(error ? 10 : 0);
}

int
CheckMemRegion(UBYTE *low, UBYTE *high)
{
	int error = 0;
	ULONG bits;
	while (low + 31 < high)
	{
		if (!((ULONG) low % 0x40000))
		{
			if (SetSignal( 0L, 0L ) & SIGBREAKF_CTRL_C)
			{
				PrintFault(ERROR_BREAK,NULL);
				return error;
			}
			if (!quiet)
			{
				putchar('.');
				fflush(stdout);
			}
		}
		
		if (bits = CheckMemBlock(low)) /* check 32 bytes.. disables interrupts! */
		{
			error = 1;
			printf("\nProblem between 0x%x .. 0x%x, bitpattern 0x%8x\n", low, low + 31, bits);
			fflush(stdout);
		}
		low += 32;
	}
	if (!quiet)
		putchar('\n');
	return error;
}
