/********** gpm.c *****************************************************/

#define GPMBUILTIN_C_

#include "gpmdefs.h"
#include "gpm.h"
#include "gpmmain.h"
#include "gpmmonitor.h"
#include "gpmbuiltin.h"


#ifdef DEBUG_MACROS
static char *begin="\n------ BEGIN %s.\n\n";
static char *end  ="\n------ END   %s.\n\n";
#endif

void do_def()
{
/*
 *	length of "DEF" + 1 = 4
 *	D
 *	E
 *	F
 *	length of first arg + 1		<-- P+6
 *	first arg
 *	length of second arg + 1
 *	second arg
 *
 */


	int start_of_args;
	int length1,length2;

	start_of_args=P+6;

	length1 = Stack[start_of_args];
	if (length1==Marker) Monitor(BadDEF1);

	length2 = Stack[start_of_args + length1];

	/*
	 * Let's say somebody types {DEF|a}. Then the stack contains
	 *
	 *
	 * D
	 * E
	 * F
	 * ^B	(value 2)
	 * a
	 * Marker
	 *
	 * Start_of_args points to the ^B. So if length2 == Marker,
	 * we've got a DEF with no second argument. Bummer.
	 */

	if (length2==Marker)
		Monitor(BadDEF);

	Stack[start_of_args + length1 + length2] = Marker;

	if (H != 0)
		Stack[H] = Stack[H] - Stack[P - 1] + 6;

	Stack[P - 1] = 6;
	Stack[P + 5] = EnvPtr;
	EnvPtr = P + 5;
}

void do_update()
{
	int r;

	/*
	 * In a call to UPDATE, the stack looks like this:
	 *
	 * UPDATE        	arg0		<-- P
	 * xxx			admin
	 * n			length of arg1
	 * arg1
	 * m
	 * arg2
	 * Marker
	 */

	Find(P + 9);
	A = P + 9 + Stack[P + 9];
	if (Stack[A] > Stack[W])
		Monitor9();
	for (r = 1; r <= Stack[A]; r++)
		Stack[W + r] = Stack[A + r];
}

void do_len()
{
	LoadC(Stack[P+6]-1);
}

void do_ifdef()
{
	int i;

	i=iFind(P+8);
	if (i<0) 	LoadC(0);
	else		LoadC(1);
}

void do_val()
{
/*
 * Lookup the macro that is the first argument,
 * then copy it (Find leaves W pointing to the
 * definition.) to the stack.
 */

	Find(P + 6);
	if (Stack[W]<0)
	{
		LoadS("<BUILTIN>");
		return;
	}
	W=W+1;

	while (Stack[W] != Marker) 
	{
		LoadC(Stack[W++]);
	}
}

void do_bin()
{
	int Value=0;
	int StackScan;

/*
 * Put the binary equivalent of argument 1 in value, and then push
 * it on the stack. The stack now looks like this:
 *
 * P -> 'B'
 *      'I'
 *      'N'
 *      x
 *      x    4 items of administration from the ArgSep
 *      x
 *      x
 *      Argument 1
 *      
 *
 * So you can start from P+7 to check the argument.
 *
 */


/*
 * If there's a leading + or -, deal with it.
 */

	if (Stack[P+7]=='+' || Stack[P+7]=='-')
	{
		StackScan=P+8;
	}
	else
	{
		StackScan=P+7;
	}

	while (Stack[StackScan] != Marker) 
	{
		int x = Num(Stack[StackScan]);
		if (x < 0 || x > 9) Monitor10();
		Value = 10 * Value + x;
		StackScan++;
	}

/*
 * Now check the leading sign, if any, again, so that
 * the correct value is pushed to the stack.
 */

	if (Stack[P+7]=='-')
	{
		Push(-Value);
	}
	else
	{
		Push(Value);
	}

#ifdef DEBUG
	fprintf(stderr,"BIN has value %d.\n",Value);
#endif
}

void do_dec()
{
	char b[16];

	if (Stack[P+6]!=2)
	{
		Monitor(BadDEC);
		LoadC('0');
		return;
	}

	W = Stack[P + 7];

	sprintf(b,"%d",W);
	LoadS(b);
}

void do_bar()
{
	int result;
	int A;

	if (Stack[P+6]!=2 ||
	    Stack[P+8]!=2 ||
	    Stack[P+10]!=2)
	{
		Monitor(BadBARcall);
		LoadC(0);
		return;
	}

	W = Stack[P + 9];
	A = Stack[P + 11];

	switch(Stack[P+7])
	{
		case '+' : result = W + A; break;
		case '-' : result = W - A; break;
		case '*' : result = W * A; break;
		case '/' : result = W / A; break;
		case 'R' :
		case '%' : result = W % A; break;
		case '=' : if (W==A) result=1; else result=0; break;
		case '<' : if (W<A) result=1; else result=0; break;
		case '>' : if (W>A) result=1; else result=0; break;
		default : Monitor(BadBAROp); result=0; break;
	}

	LoadC(result);
}


void do_sub()
{
	int begin,len;
	int argl;
	int i;

	/* Stack looks like this:
	 *
	 * SUB			<- P
	 * xxx		administration
	 * n		length of arg1
	 * arg1
	 * m		length of arg2
	 * arg2
	 * p		length of arg3
	 * arg3
	 * Marker
	 *
	 * n and m must be two (numbers resulting from BAR).
	 */


	if (Stack[P+6]!=2) Monitor(BadSUBcall);
	begin=Stack[P+7];

	if (Stack[P+8]!=2) Monitor(BadSUBcall);
	len=Stack[P+9];

	argl=Stack[P+10];			/* Length of arg3 */
	if (begin<1 || begin>argl) { Monitor(BadSUBshort); return; }

	if ((begin+len)>argl)	{ Monitor(BadSUBlong); len=argl-begin; }


	for (i=0; i<len; i++)
		LoadC(Stack[P+10+begin+i]);
}






#ifdef DEBUG_MACROS
char *do_dumpc(int i)
{
	static char b[6];

	if (i<32)
	{
		sprintf(b,"^%c",i+64);
		return b;
	}
	if (i>127)
	{
		i=i-128;
		if (i<32)
		{
			sprintf(b,"M-^%c",i+64);
			return b;
		}
		if (i<128)
		{
			sprintf(b,"M-%c",i);
			return b;
		}
		b[0]=0;
		return b;
	}

	sprintf(b,"%c",i);
	return b;
}

void do_dumpf(int count)
{
	static char *dump="Stack dump";
	int i;

	fflush(stdout);
	fprintf(stderr,begin,dump);

	fprintf(stderr,"[%4d] ",count);
	while (count<=StackPtr)
	{
		for (i=0; i<4; i++)
		{
			fprintf(stderr,"%8d (%4s)   ",
				Stack[count],do_dumpc(Stack[count]));
			count++;
		}

		if (count<=StackPtr)
			fprintf(stderr,"\n[%4d] ",count);
	}

	fprintf(stderr,"\n\nCurrent stack state is: \n\nSize=%8d Top =%8d\n",
		StackSize,StackPtr);
	fprintf(stderr,"In  =%8d Out =%8d Env =%8d\n",InputPtr,OutputPtr,EnvPtr);
	fprintf(stderr,end,dump);
}

void do_dump()
{
	/* Stack contents are:
	 * 
	 * x	<- P points here.
	 * x
	 * ^D	(4)
	 * D
	 * U
	 * M
	 * P
	 * 	<- if here is a marker, then the call was {DUMP}
	 *	   if not, then this is the length of the first
	 *	   argument. It should be two, since the argument
	 * 	   is a single *INTEGER* (result of BIN, probably).
	 * 
	 * 
	 */

	int start_args=P+7;
	int arg_len=Stack[start_args];
	int start_dump=0;


	if (arg_len==Marker)
	{
		do_dumpf(start_dump);
	}
	else
	{
		if (arg_len==2)
		{
			start_dump=Stack[start_args+1];
			if (start_dump>StackPtr || start_dump<0)
			{
				Monitor(BadDUMPrange);
			}
			else
			{
				do_dumpf(start_dump);
			}
		}
		else
		{
			do_dumpf(start_dump);
			Monitor(BadDUMP);
		}
	}
}

void do_output(FILE *stream)
{
#define ERRBUFSIZE	(128)

	static char b[ERRBUFSIZE+10];
	int bp=0;
	int scan=P+7;

	while(Stack[scan]!=Marker)
	{
		b[bp]=(char)Stack[scan++];
		bp++;
		if (bp==ERRBUFSIZE)
		{
			b[bp]=0;
			fprintf(stream,"%s",b);
			bp=0;
		}
	}
	if (bp>0)
	{
		b[bp]=0;
		fprintf(stream,"%s",b);
	}
#undef ERRBUFSIZE
}

void do_msg()
{
	fflush(stdout);
	do_output(stderr);
}

void do_exit()
{
	Monitor(Exit);
}

#endif
