/*		VBR.c
 *  
 *		(c) Copyright 1993 Klaus Weber. Freely distributable.
 *
 *		VBR allows you to move the Interrupt-Vector-Table (normally located
 *		at address 0) into FastRAM, thereby improving e.g. serial IO Per-
 *		formance.
 *
 *		Type  VBR ?  for a list of available options. See the documentation
 *		for further details.
 *
 *		Compiles under SAS/C V6.2. Tabs are set every 3 columns.
 *
 *		REVISION HISTORY:
 *		-----------------
 *		28/03/93:	37.1	First version.
 *		30/03/93:	37.2	- Fixed stupid bug: forgot to close dos.library :-<
 *								- minor code cleanup
 */

#include	"VBR.h"

#define NAME					"VBR"
#define VERSION				"37"
#define REVISION				"2"
#define DATE					"30.3.93"

#define TEMPLATE				"SHOW/S,SET/S,RESET/S,FORCE/S,QUIET/S"
#define OPT_SHOW				0
#define OPT_SET				1
#define OPT_RESET				2
#define OPT_FORCE				3
#define OPT_QUIET				4
#define OPT_COUNT				5

#define VBRCOOKIE				"VBR magic cookie"
#define NUMBEROFVECTORS		256

#define DOSLIB					"dos.library"
#define DOSVER					37L			/* We require AT LEAST V37 of OS */


/*
 *		Unfortunately, this:
 *
 *			char *VersTag = "$VER: " NAME " (" __DATE__ ")";
 *
 *		does not work, because SAS/C generates something like
 *		"Mar 28 1993" instead of "28.3.93".
 */
#define FULLNAME	NAME " " VERSION "." REVISION
char *VersTag = "$VER: " FULLNAME " (" DATE ")";
char *Copyright = "© 1993 Klaus Weber. Freely distributable.";

/*
 *		This is going to be the new vectortable
 */
struct VBRTable {
	ULONG		Vector[NUMBEROFVECTORS];	/* Space for the vectors */
	char		cookie[sizeof(VBRCOOKIE)];	/* the magic cookie */
};

struct VBRTable *myVBRTable;

/*
 *		This is the option array for ReadArgs().
 */

LONG	opts[OPT_COUNT];

/*
 *		This is for the processor type checking
 */
UWORD	attnflags;

struct	DosLibrary	*DOSBase;

/*
 *		Two macros to set pr_Result2
 */
#define THISPROC   ((struct Process *)(SysBase->ThisTask))
#define Result2(x) THISPROC->pr_Result2 = x

/*
 *		The returncode for scriptfiles
 */
int	rc = RETURN_OK;

/*
 * main _has_ to be the first function in this module in order to be
 * able to omit the startup module. Additionally, it has to be declared
 * with __saveds and has to be the first object module in the slink
 * statement.
 */
int __saveds main ( VOID )

{
	struct	RDArgs		*argsptr;
	APTR						current_VBR,TableMem;
	struct	ExecBase 	*SysBase = (*((struct ExecBase **) 4));

	/*
	 * If dos.library V37+ fails to open, quit immediately.
	 */
	if ( !(DOSBase = (struct DosLibrary *)OpenLibrary(DOSLIB, DOSVER)) ) {
		Result2(ERROR_INVALID_RESIDENT_LIBRARY);
		return RETURN_FAIL;
	}

	/*
	 * Clear the options array (remember, we have no startup code!).
	 */
	memset((char *)opts, 0, sizeof(opts));

	/*
	 * If ReadArgs() sees anything but zeros passed to it in elements
	 * of this array, ReadArgs() will assume that they are defaults.
	 */
	argsptr = ReadArgs(TEMPLATE, opts, NULL);

	/*
	 * argsptr will be NULL if ReadArgs() failed, the secondary result
	 * code is fetched by IoErr().
	 */
	if ( argsptr == NULL ) {
		PrintFault(IoErr(), NULL);	/* prints the appropriate err message */
		rc = RETURN_ERROR;
	} else {
		attnflags = SysBase -> AttnFlags;

		if ( !(attnflags & AFF_68010) ) {
			myVPrintf("This program requires a MC 68010 or better\n",NULL);
			rc = RETURN_ERROR;
		} else {

			if ( opts[OPT_SET] && opts[OPT_RESET] ) {
				/*
				 *		Obviously, the user has not yet decided what (s)he
				 *		wants, so just print an error message and abort.
				 */
				myVPrintf("SET and RESET are mutually exclusive\n",NULL);
				rc = RETURN_ERROR;
			} else {

				if ( opts[OPT_FORCE] && !opts[OPT_RESET] ) {
					/*
					 * FORCE makes no sense without RESET, so lets quit with an error.
					 */
					myVPrintf("FORCE makes no sense without RESET\n",NULL);
					rc = RETURN_ERROR;
				} else {

					if ( opts[OPT_SHOW] || ((!opts[OPT_SET]) && (!opts[OPT_RESET])) ) {
						/*
						 * Report the current VBR setting.
						 */
						current_VBR = GetVBR();
						myVPrintf("Current VBR address: 0x%lx\n",(ULONG *)&current_VBR);
					}

					if ( opts[OPT_SET] ) {
						/*
						 * Before we try to move the Vectortable into FastRAM, lets
						 * check if it is already there (either done by VBR or by an
						 * alien program such as Enforcer).
						 */
						current_VBR = GetVBR();
						if ( current_VBR == NULL ) {
							/*
							 * So the Vectortable is still in ChipRAM.
							 */
							TableMem = AllocMem(sizeof(struct VBRTable),MEMF_FAST|MEMF_PUBLIC|MEMF_CLEAR);
							if ( !TableMem ) {
								/*
								 * We did not get the memory for the new vectortable. This
								 * may be because there is no FastRAM in the system, or
								 * there simply is not enough free memory. There is nothing
								 * we can do then, so lets quit.
								 */
								myVPrintf("Unable to allocate memory for new vectortable\n",NULL);
								rc = RETURN_ERROR;
							} else {
								/*
								 * We got the memory for the vectortable. First, we copy
								 * the vectors, then we fill in the magic cookie, so we
								 * know who has set up the VBR.
								 */
								CopyMemQuick(current_VBR,TableMem,NUMBEROFVECTORS*sizeof(ULONG));
								myVBRTable = TableMem;
								strcpy(myVBRTable->cookie,VBRCOOKIE); /* Place the magic cookie */
								SetVBR(myVBRTable);
								myVPrintf("New VBR address: 0x%lx\n",(ULONG *)&myVBRTable);
							}
						} else {
							/*
							 * The VBR is already set. We check if this has been done by
							 * VBR (by searching for the magic cookie) or by an alien program
							 * (such as Enforcer) and inform the user.
							 */
							myVPrintf("VBR already set by ",NULL);
							if ( strcmp(((struct VBRTable *)current_VBR)->cookie,VBRCOOKIE)==0 ) {
								myVPrintf(FULLNAME "\n",NULL);			/* cookie found */
							} else {
								myVPrintf("alien program\n",NULL);		/* no cookie found */
							}
						}
					}

					if ( opts[OPT_RESET] ) {
						/*
						 * First check if the vectortable is located in FastRAM currently.
						 */
						current_VBR = GetVBR();
						if ( current_VBR == NULL ) {
							/*
							 * The VectorBaseRegister is NULL. Inform the user and quit.
							 */
							myVPrintf("VBR not set\n",NULL);
						} else {
							/*
							 * The VectorBaseRegister does not point to location 0.
							 * If we moved the vectortable there we can safely move the
							 * VBR back to 0 and deallocate the memory. If another
							 * program has put it there, we must not deallocate the memory.
							 * In fact, we won't even touch the VBR in that case (unless
							 * the user told us with the FORCE option).
							 *
							 * We check for the magic cookie to determine the VBR setup.
							 */
							if ( strcmp(((struct VBRTable *)current_VBR)->cookie,VBRCOOKIE)==0 ) {
								/*
								 * Cookie found: We did the current VBR setup. Reset VBR
								 * and deallocate the memory.
								 */
								myVBRTable = current_VBR;
								CopyMemQuick(current_VBR,NULL,NUMBEROFVECTORS*sizeof(ULONG));
								SetVBR(NULL);
								FreeMem(myVBRTable,sizeof(struct VBRTable));
								myVPrintf("VBR reset\n",NULL);
							} else {
								/* 
								 * No cookie there: We must not return the memory to the
								 * system.
								 */
								if ( opts[OPT_FORCE] ) {
									/*
									 * Although we did not set up the vectortable, the user
									 * wants us to reset the VBR. Note that this means that
									 * the memory for the vectortable probably can never be
									 * returned to the system (until the user reboots).
									 */
									myVBRTable = current_VBR;
									CopyMemQuick(current_VBR,NULL,NUMBEROFVECTORS*sizeof(ULONG));
									SetVBR(NULL);
									myVPrintf("Alien VBR setup reset, some memory is lost\n",NULL);
								} else {
									/*
									 * No FORCE option: Just tell the user why we could not
									 * do anything.
									 */
									myVPrintf("Unable to reset VBR: alien VBR setup detected\n",NULL);
									rc = RETURN_ERROR;
								}
							}
						}
					}
				}
			}
		}
		FreeArgs(argsptr);
	}
	CloseLibrary((struct Library *)DOSBase);
	return rc;
}


/*
 *		My replacement for dos.library/VPrintf. Won't print anything if the
 *		user has specified QUIET option.
 */
LONG __asm myVPrintf(register __d1 char *fmt, register __d2 LONG *argv)
{
	if ( opts[OPT_QUIET] ) {
		return 0;
	} else {
		return VPrintf(fmt,argv);
	}
}
