/*
 * File: misc.c
 *
 * Miscellaneous routines for IMP80 compiler
 *
 * Bob Eager   August 2002
 *
 */

#include "imp.h"
#include "opcode.h"
#include "global.h"
#include "global2.h"

/* Prototypes */

static	INT		more_space(VOID);
static	VOID		print_use(VOID);

/*
 * Routine to insert a cell containing the values 's1', 's2' and 's3'
 * at the bottom of a list, updating top and bottom pointers appropriately.
 *
 */

VOID binsert(PINT top, PINT bot, INT s1, INT s2, INT s3)
{	INT i, j;
	LISTF *lcell;

	i = asl;
	if(i == 0) i = more_space();
	lcell = &aslist[i];
	asl = lcell->link;
	lcell->s1 = s1;
	lcell->s2 = s2;
	lcell->s3 = s3;
	lcell->link = 0;
	j = *bot;
	if(j == 0) {
		*bot = i;
		*top = *bot;
	} else {
		aslist[j].link = i;
		*bot = i;
	}
}


/*
 * Routine to check the asl and output the number of free cells.
 *
 */

#if	DEBUG
VOID check_asl(VOID)
{	INT num = 0;
	INT qq = asl;

	while(qq != 0) {
		num++;
		qq = aslist[qq].link;
	}

	fprintf(listfp, "\nFree cells after line %d = %d", line, num);
}
#endif


/*
 * Routine to throw away a complete list (which may be null), and
 * reset the listhead.
 *
 */

VOID clear_list(PINT ophead)
{	INT i, j;

	i = *ophead;
	j = i;
	while(i != 0) {
		j = i;
		i = aslist[j].link;
	}
	if(j != 0) {
		aslist[j].link = asl;
		asl = *ophead;
		*ophead = 0;
	}
}


/*
 * Function to compare two IMP-style strings.
 *
 */

INT eqstring(PUCHAR s1, PUCHAR s2)
{	UINT len;

	len = s1[0];
	if((len != s2[0]) || (strncmp(&s1[1], &s2[1], len) != 0))
		return(NO);

	return(YES);
}


/*
 * Routine to set up an error message - this is sent to the listing
 * and optionally to the terminal
 *
 */

VOID fault(INT n, UINT data, UINT ident)
{	INT i, j, t, ll, s;
	UINT info;
	FILE *fp;
	PUCHAR sp;
	PUCHAR datap = (PUCHAR) data;
	UCHAR mess1[256];
	UCHAR mess2[256];
	UCHAR temp[256];
	UCHAR name[256];
	UCHAR c;

	mess1[0] = '\0';
	mess2[0] = '\0';
	faulty++;

	if(n == 100) {			/* Syntax faults are special */
		sprintf(mess1, "\n*    Failed to analyse line %d\n     ", line);
		if(line != oldline) {
			strcat(mess1,
		"Text mode failure - erroneous source line not available\n");
		} else {
			ll = j = s = t = 0;
			do {
				i = j;
				j = datap[q];
				if((j > 128) && (i < 128)) {
					mess2[ll++] = ' ';
					mess2[ll++] = '%';
					t += 2;
				}
				if((i > 128) && (j < 128))
					mess2[t++, ll++] = ' ';
				mess2[ll++] = (UCHAR) (j & 0x7f);
				t++;
				if(q == qmax) s = t;
				q++;
				if(t >= 250) break;
			} while (!(((j == ';') &&
				 (q > qmax)) || (q == length)));
			if(q == qmax) s = t;
			mess2[ll] = '\0';
        	}
	} else {
		sprintf(mess1, "\n* %4d   Fault %d", line, n);
		parmopt = YES;
		if(parmlet == NO) inhcode = YES;
					/* Stop generating code */
		message(mess2, n);

		sp = strpbrk(mess2, "#&");
		if((sp != (PUCHAR) NULL)) {
			c = *sp;
			*sp++ = '\0';	/* Terminate first part */
			if(c == '#') {
				if(*sp == '#') {
					sp++;
					/* Point to new tail */
					info = ident;
				} else info = data;
				sprintf(temp, "%s%d%s", mess2, info, sp);
			} else {	/* Must be '&' */
				if(*sp == '&') {
					sp++;
					/* Point to new tail */
					sprintf(temp, "%s%s%s",
						mess2,
						printname(name, (INT) data),
						sp);
				} else {
					sprintf(temp, "%s%s%s",
						mess2,
						printname(name, (INT) ident),
						sp);
					if(n > 100) strcat(mess2, " Disaster");
				}
			}
			strcpy(mess2, temp);
		}
	}

	fp = listfp;
	for(i = 1; i <= 2; i++) {
		fputs(mess1, fp);
		if(mess2[0] != '\0') fputs(mess2, fp);

		if((n == 100) && (s < 115)) {
			fputc('\n', fp);
			for(j = 1; j <= s + 4; j++) fputc(' ', fp);
			fputc('!', fp);
		}
		fputc('\n', fp);
		fp = stderr;		/* For second time around */
	}
	if(n > 100) iexit(EXIT_FAILURE);/* Fatal error */
}


/*
 * Routine to scan the list 'list' looking for 'lab' in the 's2' field,
 * returning the corresponding cell number.
 * This is used for more than just scanning label lists.
 *
 */

INT find(INT lab, INT list)
{	while(list != 0) {
		if(lab == aslist[list].s2)
			return(list);
		list = aslist[list].link;
	}
	return(-1);
}


/*
 * Routine to return the 's1' and 's2' values from the
 * cell 'cell'.
 *
 */

VOID from12(INT cell, PINT s1, PINT s2)
{	LISTF *lcell = &aslist[cell];

	*s1 = lcell->s1;
	*s2 = lcell->s2;
}


/*
 * Routine to return the 's1', 's2' and 's3' values from the
 * cell 'cell'.
 *
 */

VOID from123(INT cell, PINT s1, PINT s2, PINT s3)
{	LISTF *lcell = &aslist[cell];

	*s1 = lcell->s1;
	*s2 = lcell->s2;
	*s3 = lcell->s3;
}


/*
 * Function to return the 2-byte value at 'a[ptr]'.
 *
 */

INT fromar2(INT ptr)
{	INT value;

	value = a[ptr];
	value = value | (a[ptr+1] << 8);
	return(value);
}


/*
 * Function to return the 4-byte value at 'a[ptr]'.
 *
 */

INT fromar4(INT ptr)
{	INT res = 0;
	INT i;

	for(i = 0; i <= 3; i++)
		res = res | (a[ptr+i] << ((3-i)*8));
	return(res);
}


/*
 * Routine to find or create a temporary variable of 'size' words.
 *
 */

VOID getwsp(INT place, INT size)
{	size = 0;		/* Protem */
	place = 0;		/* Protem */
	return;
}


/*
 * Routine to convert an IMP-style string to a C-style string.
 *
 */

VOID imptoc(PUCHAR cs, PUCHAR imps)
{	UINT len = imps[0];

	strncpy(&cs[0], &imps[1], len);
	cs[len] = '\0';
}


/*
 * Routine to add a cell to the bottom of the list headed by 'cell'.
 *
 */

VOID insert_at_end(PINT cell, INT s1, INT s2, INT s3)
{	INT i, j, nn;
	LISTF *lcell;

	i = *cell;
	j = i;
	while(i != 0) {
		j = i;
		i = aslist[j].link;
	}
	nn = asl;
	if(nn == 0) nn = more_space();
	lcell = &aslist[nn];
	asl = lcell->link;
	if(j == 0) *cell = nn; else aslist[j].link = nn;
	lcell->s1 = s1;
	lcell->s2 = s2;
	lcell->s3 = s3;
	lcell->link = 0;
}


/*
 * Routine to return the next cell in a list.
 *
 */

VOID mlink(PINT cell)
{	*cell = aslist[*cell].link;
}


/*
 * Routine to format up some more of the asl.
 *
 */

static INT more_space(VOID)
{	INT i, nn, cl;
	INT amount;

	nn = aslcurbtm - 1;		/* Top of free space */
	amount = (nnames + 1)/8;	/* One eighth of 'nnames' */
	i = aslcurbtm - ((const_ptr + 8)/4);
					/* Gap between consts and asl */
	if(i/2 < amount) {
		amount = i/2;
		aslwarn = 1;		/* Half the gap max */
	}
	if(amount < 20) amount = 0;
	aslcurbtm -= amount;		/* Move down to make space */
	if(aslcurbtm < 1) aslcurbtm = 1;
	cl = sizeof(INT)*aslcurbtm - 8;	/* New constant limit */
	if((aslcurbtm >= nn) || (const_ptr > cl)) {
		aslcurbtm = nn + 1;	/* As you were */
		for(i = 1; i <= 12; i++) {
			if(dvheads[i] != 0)
				clear_list(&dvheads[i]);
		}
		if(asl != 0) return(asl);
		fault(102, (UINT) wkfilek, 0);	/* Workfile too small */
	} else const_limit = cl;	/* New value with bigger asl */
	for(i = aslcurbtm; i < nn; i++)
		aslist[i+1].link = i;	/* Format the new piece */
	aslist[aslcurbtm].link = 0;
	asl = nn;
	return(nn);
}


/*
 * Routine to output register usage.
 *
 */

static VOID print_use(VOID)
{
	return;
}


/*
 * Routine to copy the information from the top cell of list 'cell'
 * into 's1', 's2' and 's3, and then pop the list up one cell.
 * Returns -1s if list empty.
 *
 */

VOID pop(PINT cell, VOID *s1, VOID *s2, VOID *s3)
{	INT i;
	LISTF *lcell;

	i = *cell;
	lcell = &aslist[i];
	*((PINT) s1) = lcell->s1;
	*((PINT) s2) = lcell->s2;
	*((PINT) s3) = lcell->s3;
	if(i != 0) {
		*cell = lcell->link;
		lcell->link = asl;
		asl = i;
	}
}


/*
 * Routine to output the contents of the cells in the list headed by 'head'.
 *
 */

#if	DEBUG
VOID print_list(INT head)
{	INT i, j, k;

	fprintf(listfp, "\nPrint of list %d\n", head);

	while(head != 0) {
		from123(head, &i, &j, &k);
		fprintf(listfp,
			"%3d   %08X   %08X   %08X\n", head, i, j, k);
		mlink(&head);
		head &= 0xffff;		/* Extra link in tags list!! */
	}
}
#endif


/*
 * Function to return a string representing a name in the dictionary.
 * The string is returned in the parameter 's', and the result is also
 * a pointer to 's'.
 *
 */

PCHAR printname(PCHAR s, INT n)
{	INT v, k;

	strcpy(s, "???");		/* In case nothing found */

	if(0 <= n && n <= nnames) {
		v = word[n];
		k = dictbase[v];
		if(k != 0)
			strcpy(s, (PCHAR)(dictbase+v));
	}
	return(s);
}


/*
 * Routine to push a cell containing the values 's1', 's2' and 's3'
 * onto the top of the list pointed to by 'cell'.
 *
 */

VOID push(PINT cell, INT s1, INT s2, INT s3)
{	INT i;
	LISTF *lcell;

	i = asl;
	if(i == 0) i = more_space();
	lcell = &aslist[i];
	asl = lcell->link;
	lcell->link = *cell;
	*cell = i;
	lcell->s1 = s1;
	lcell->s2 = s2;
	lcell->s3 = s3;
}


/*
 * Routine to set the 's1', 's2' and 's3' values in the cell 'cell'.
 *
 */

VOID replace123(INT cell, INT s1, INT s2, INT s3)
{	LISTF *lcell = &aslist[cell];

	lcell->s1 = s1;
	lcell->s2 = s2;
	lcell->s3 = s3;
}


/*
 * Returns workspace to ordered free list. Addressable cells are put at
 * the top, non-addressable on the back.
 *
 */

VOID returnwsp(INT place, INT size)
{	place = 0;		/* Protem */
	size = 0;		/* Protem */
	return;
}


/*
 * Routine to put the 2-byte value 'value' into 'a' at 'ptr'.
 *
 */

VOID toar2(INT ptr, INT value)
{	a[ptr] = (UCHAR) (value & 0xff);
	a[ptr+1] = (UCHAR) (value >> 8);
}


/*
 * Routine to put the 4-byte value 'value' into 'a' at 'ptr'.
 *
 */

VOID toar4(INT ptr, INT value)
{	INT i;

	for(i = 0; i <= 3; i++)
		a[ptr+i] = (UCHAR) ((value >> ((3-i)*8)) & 0xff);
}


/*
 * Routine to put the 8-byte value 'value' into 'a' at 'ptr'.
 *
 */

VOID toar8(INT ptr, PUCHAR value)
{	INT i;

	for(i = 0; i <= 7; i++)
		a[ptr+i] = value[i];
}


/*
 * Routine to output a warning message.
 *
 */

VOID warn(INT n, UINT v)
{	UCHAR s[121];
	UCHAR temp[256];
	UCHAR name[256];
	PUCHAR sp;

	message(s, n+200);
	sp = strpbrk(s, "&");
	if(sp != (PUCHAR) NULL) {
		*sp++ = '\0';		/* Split the message */
		sprintf(temp, "%s%s%s", s, printname(name, (INT) v), sp);
		strcpy(s, temp);
	}
	fprintf(listfp, "\n?  Warning :- %s at line No %d\n", s, line);
}


/*
 * Routine to call 'malloc' checking reply; if call fails then an error
 * message is displayed and the program exits.
 *
 */

PCHAR xalloc(INT n)
{	PUCHAR res;

	res = malloc((UINT) n);

	if(res == (PUCHAR) NULL) {
		fprintf(stderr, "%s: cannot allocate memory\n", progname);
		iexit(EXIT_FAILURE);
	}
	return(res);
}


/*
 * Routine to abort the compilation and output useful diagnostic
 * information.
 *
 */

VOID xabort(VOID)
{	static PCHAR stars = "**********";

	fprintf(stderr, "*** Abort ***\n");
	fprintf(listfp, "\n%s Abort %s Abort %s Abort %s\n",
			stars, stars, stars, stars);

#if	DEBUG
	if(ca != cabuf)
		ncode(&code[0], &code[ppcurr], (UINT) cabuf);
	print_use();
	abort();
#endif
}


/*********************************************************************
 *	Packing and unpacking routines for 'ptype'.                  *
 ********************************************************************/

/* Layout of 'ptype':
 *
 * 'ptype' requires 16 bits to define a variable, and can be regarded as
 * as two bytes:
 *	upper one(uptype)	:= litl<<6!rout<<4!nam<<2!arr
 *	lower one(ptype)	:= prec<<4!type
 *
 * Often (e.g. in 'expop') only the lower part is required, as functions
 * etc. are prefetched and stacked.
 *
 * litl		:= 1=const,2=external,3=extrinsic(or dynamic), 0=none of these
 * rout		:= 1 for routine or fn or map, =0 none of these
 * nam		:= 2 for maps and 'refrefs',=1 for names ,=0 directly addressed
 * arr		:= 1 for arrays =0 scalars
 * prec		   is descriptor size code for each precision:
 *			:=0 bits, =3 bytes, =5 words, =6 d-wrds, =7,quad wrds
 * type		:= the variable type 
 *			:=0 (type general), =1 integer, =2 real, =3 record
 *			:=4 (recordformat), =5 string,  =6 label/switch.
 *			:=7 not set
 *
 */


/*
 * Routine to unpack 'ptype' into its component parts.
 *
 */

VOID unpack(VOID)
{	litl = ptype >> 14;
	rout = (ptype >> 12) & 0x03;
	nam = (ptype >> 10) & 0x03;
	arr = (ptype >> 8) & 0x03;
	prec = (ptype >> 4) & 0x0f;
	type = ptype & 0x0f;
}


/*
 * Routine to pack a 'ptype' value, but into a place specified by the
 * parameter.
 *
 */

VOID pack(PINT ptype)
{	*ptype = ((litl & 0x03) << 14) |
		 ((rout & 0x03) << 12) |
	 	 ((nam  & 0x03) << 10) |
		 ((arr  & 0x03) <<  8) |
		 ((prec & 0x0f) <<  4) |
		 (type & 0x0f);
}

/*
 * End of file: misc.c
 *
 */
