/* asexpr.c */

/*
 * (C) Copyright 1989-2000
 * All Rights Reserved
 *
 * Alan R. Baldwin
 * 721 Berkeley St.
 * Kent, Ohio  44240
 */

#include <stdio.h>
#include <setjmp.h>
#include <string.h>

#ifdef WIN32
#include <stdlib.h>
#else
#include <alloc.h>
#endif

#include "asxxxx.h"

/*)Module	asexpr.c
 *
 *	The module asexpr.c contains the routines to evaluate
 *	arithmetic/numerical expressions.  The functions in
 *	asexpr.c perform a recursive evaluation of the arithmetic
 *	expression read from the assembler-source text line.
 *	The expression may include binary/unary operators, brackets,
 *	symbols, labels, and constants in hexadecimal, decimal, octal
 *	and binary.  Arithmetic operations are prioritized and
 *	evaluated by normal arithmetic conventions.
 *
 *	asexpr.c contains the following functions:
 *		VOID	abscheck()
 *		addr_t	absexpr()
 *		VOID	clrexpr()
 *		int	digit()
 *		VOID	expr()
 *		int	oprio()
 *		VOID	term()
 *
 *	asexpr.c contains no local/static variables
 */

/*)Function	VOID	expr(esp, n)
 *
 *		expr *	esp		pointer to an expr structure
 *		int	n		a firewall priority; all top
 *					level calls (from the user)
 *					should be made with n set to 0.
 *
 *	The function expr() evaluates an expression and
 *	stores its value and relocation information into
 *	the expr structure supplied by the user.
 *
 *	Notes about the arithmetic:
 *		The coding emulates 16-Bit unsigned
 *		arithmetic operations.  This allows
 *		program compilation without regard to the
 *		intrinsic integer length of the host
 *		machine.
 *
 *	local variables:
 *		addr_t	ae		value from expr esp
 *		addr_t	ar		value from expr re
 *		int	c		current assembler-source
 *					text character
 *		int	p		current operator priority
 *		area *	ap		pointer to an area structure
 *		exp	re		internal expr structure
 *
 *	global variables:
 *		char	ctype[]		array of character types, one per
 *					ASCII character
 *
 *	functions called:
 *		VOID	abscheck()	asexpr.c
 *		VOID	clrexpr()	asexpr.c
 *		VOID	expr()		asexpr.c
 *		int	get()		aslex.c
 *		int	getnb()		aslex.c
 *		int	oprio()		asexpr.c
 *		VOID	qerr()		assubr.c
 *		VOID	rerr()		assubr.c
 *		VOID	term()		asexpr.c
 *		VOID	unget()		aslex.c
 *
 *
 *	side effects:
 *		An expression is evaluated modifying the user supplied
 *		expr structure, a sym structure maybe created for an
 *		undefined symbol, and the parse of the expression may
 *		terminate if a 'q' error occurs.
 */

VOID
expr(esp, n)
register struct expr *esp;
int n;
{
	register addr_t ae, ar;	
	int c, p;
	struct area *ap;
	struct expr re;

	term(esp);
	while (ctype[c = getnb()] & BINOP) {
		/*
		 * Handle binary operators + - * / & | % ^ << >>
		 */
		if ((p = oprio(c)) <= n)
			break;
		if ((c == '>' || c == '<') && c != get())
			qerr();
		clrexpr(&re);
		expr(&re, p);
		esp->e_rlcf |= re.e_rlcf;
		
		/*
		 * 16-Bit Unsigned Arithmetic
		 */
		ae = esp->e_addr & 0xFFFF;
		ar = re.e_addr & 0xFFFF;

		if (c == '+') {
			/*
			 * esp + re, at least one must be absolute
			 */
			if (esp->e_base.e_ap == NULL) {
				/*
				 * esp is absolute (constant),
				 * use area from re
				 */
				esp->e_base.e_ap = re.e_base.e_ap;
			} else
			if (re.e_base.e_ap) {
				/*
				 * re should be absolute (constant)
				 */
				rerr();
			}
			if (esp->e_flag && re.e_flag)
				rerr();
			if (re.e_flag)
				esp->e_flag = 1;
			ae += ar;
		} else
		if (c == '-') {
			/*
			 * esp - re
			 */
			if ((ap = re.e_base.e_ap) != NULL) {
				if (esp->e_base.e_ap == ap) {
					esp->e_base.e_ap = NULL;
				} else {
					rerr();
				}
			}
			if (re.e_flag)
				rerr();
			ae -= ar;
		} else {
			/*
			 * Both operands (esp and re) must be constants
			 */
			abscheck(esp);
			abscheck(&re);
			switch (c) {
			/*
			 * The (int) /, %, and >> operations
			 * are truncated to 2-bytes.
			 */
			case '*':
				ae *= ar;
				break;

			case '/':
				if (ar == 0) {
					ae = 0;
					err('z');
				} else {
					ae /= ar;
				}
				break;

			case '&':
				ae &= ar;
				break;

			case '|':
				ae |= ar;
				break;

			case '%':
				if (ar == 0) {
					ae = 0;
					err('z');
				} else {
					ae %= ar;
				}
				break;

			case '^':
				ae ^= ar;
				break;

			case '<':
				ae <<= ar;
				break;

			case '>':
				ae >>= ar;
				break;

			default:
				qerr();
				break;
			}
		}
		esp->e_addr = (ae & 0x8000) ? ae | ~0x7FFF : ae & 0x7FFF;
	}
	unget(c);
}

/*)Function	addr_t	absexpr()
 *
 *	The function absexpr() evaluates an expression, verifies it
 *	is absolute (i.e. not position dependent or relocatable), and
 *	returns its value.
 *
 *	local variables:
 *		expr	e		expr structure
 *
 *	global variables:
 *		none
 *
 *	functions called:
 *		VOID	abscheck()	asexpr.c
 *		VOID	clrexpr()	asexpr.c
 *		VOID	expr()		asexpr.c
 *
 *	side effects:
 *		If the expression is not absolute then
 *		a 'r' error is reported.
 */

addr_t
absexpr()
{
	struct expr e;

	clrexpr(&e);
	expr(&e, 0);
	abscheck(&e);
	return (e.e_addr);
}

/*)Function	VOID	term(esp)
 *
 *		expr *	esp		pointer to an expr structure
 *
 *	The function term() evaluates a single constant
 *	or symbol value prefaced by any unary operator
 *	( +, -, ~, ', ", >, or < ).  This routine is also
 *	responsible for setting the relocation type to symbol
 *	based (e.flag != 0) on global references.
 *
 *	Notes about the arithmetic:
 *		The coding emulates 16-Bit unsigned
 *		arithmetic operations.  This allows
 *		program compilation without regard to the
 *		intrinsic integer length of the host
 *		machine.
 *
 *	local variables:
 *		int	c		current character
 *		char	id[]		symbol name
 *		char *	jp		pointer to assembler-source text
 *		int	n		constant evaluation running sum
 *		int	r		current evaluation radix
 *		sym *	sp		pointer to a sym structure
 *		tsym *	tp		pointer to a tsym structure
 *		int	v		current digit evaluation
 *
 *	global variables:
 *		char	ctype[]		array of character types, one per
 *					ASCII character
 *		sym *	symp		pointer to a symbol structure
 *
 *	functions called:
 *		VOID	abscheck()	asexpr.c
 *		int	digit()		asexpr.c
 *		VOID	err()		assubr.c
 *		VOID	expr()		asexpr.c
 *		int	is_abs()	asexpr.c
 *		int	get()		aslex.c
 *		VOID	getid()		aslex.c
 *		int	getmap()	aslex.c
 *		int	getnb()		aslex.c
 *		sym *	lookup()	assym.c
 *		VOID	qerr()		assubr.c
 *		VOID	unget()		aslex.c
 *
 *	side effects:
 *		An arithmetic term is evaluated, a symbol structure
 *		may be created, term evaluation may be terminated
 *		by a 'q' error.
 */

VOID
term(esp)
register struct expr *esp;
{
	register int c, n;
	register char *jp;
	char id[NCPS];
	struct sym  *sp;
	struct tsym *tp;
	int r, v;

	r = radix;
	c = getnb();
	/*
 	 * Discard the unary '+' at this point and
	 * also any reference to numerical arguments
	 * associated with the '#' prefix.
	 */
	while (c == '+' || c == '#') { c = getnb(); }
	/*
 	 * Evaluate all binary operators
	 * by recursively calling expr().
	 */
	if (c == LFTERM) {
		expr(esp, 0);
		if (getnb() != RTTERM)
			qerr();
		return;
	}
	if (c == '-') {
		expr(esp, 100);
		abscheck(esp);
		esp->e_addr = ~esp->e_addr + 1;
		return;
	}
	if (c == '~') {
		expr(esp, 100);
		abscheck(esp);
		esp->e_addr = ~esp->e_addr;
		return;
	}
	if (c == '\'') {
		esp->e_mode = S_USER;
		esp->e_addr = getmap(-1)&0377;
		return;
	}
	if (c == '\"') {
		esp->e_mode = S_USER;
		if (hilo) {
		    esp->e_addr  = (getmap(-1)&0377)<<8;
		    esp->e_addr |= (getmap(-1)&0377);
		} else {
		    esp->e_addr  = (getmap(-1)&0377);
		    esp->e_addr |= (getmap(-1)&0377)<<8;
		}
		if (esp->e_addr & 0x8000) {
			esp->e_addr |= ~0x7FFF;
		} else {
			esp->e_addr &=  0x7FFF;
		}
		return;
	}
	if (c == '>' || c == '<') {
		expr(esp, 100);
		if (is_abs (esp)) {
			/*
			 * evaluate msb/lsb directly
			 */
			if (c == '>')
				esp->e_addr >>= 8;
			esp->e_addr &= 0377;
			return;
		} else {
			/*
			 * let linker perform msb/lsb, lsb is default
			 */
			esp->e_rlcf |= R_BYT2;
			if (c == '>')
				esp->e_rlcf |= R_MSB;
			return;
		}
	}
	/*
	 * Evaluate digit sequences as local symbols
	 * if followed by a '$' or as constants.
	 */
	if (ctype[c] & DIGIT) {
		esp->e_mode = S_USER;
		jp = ip;
		while (ctype[*jp & 0x007F] & RAD10) {
			jp++;
		}
		if (*jp == '$') {
			n = 0;
			while ((v = digit(c, 10)) >= 0) {
				n = 10*n + v;
				c = get();
			}
			tp = symp->s_tsym;
			while (tp) {
				if (n == tp->t_num) {
					esp->e_base.e_ap = tp->t_area;
					esp->e_addr = tp->t_addr;
					return;
				}
				tp = tp->t_lnk;
			}
			err('u');
			return;
		}
		if (c == '0') {
			c = get();
			switch (c) {
				case 'b':
				case 'B':
					r = 2;
					c = get();
					break;
				case 'o':
				case 'O':
				case 'q':
				case 'Q':
					r = 8;
					c = get();
					break;
				case 'd':
				case 'D':
					r = 10;
					c = get();
					break;
				case 'h':
				case 'H':
				case 'x':
				case 'X':
					r = 16;
					c = get();
					break;
				default:
					break;
			}
		}
		n = 0;
		while ((v = digit(c, r)) >= 0) {
			n = r*n + v;
			c = get();
		}
		unget(c);
		esp->e_addr = (n & 0x8000) ? n | ~0x7FFF : n & 0x7FFF;
		return;
	}
	/*
	 * Evaluate '$' sequences as a temporary radix
	 * if followed by a '%', '&', '#', or '$'.
	 */
	if (c == '$') {
		c = get();
		if (c == '%' || c == '&' || c == '#' || c == '$') {
			switch (c) {
				case '%':
					r = 2;
					break;
				case '&':
					r = 8;
					break;
				case '#':
					r = 10;
					break;
				case '$':
					r = 16;				
					break;
				default:
					break;
			}
			c = get();
			n = 0;
			while ((v = digit(c, r)) >= 0) {
				n = r*n + v;
				c = get();
			}
			unget(c);
			esp->e_mode = S_USER;
			esp->e_addr = (n & 0x8000) ? n | ~0x7FFF : n & 0x7FFF;
			return;
		}
		unget(c);
		c = '$';
	}
	/*
	 * Evaluate symbols and labels
	 */
	if (ctype[c] & LETTER) {
		esp->e_mode = S_USER;
		getid(id, c);
		sp = lookup(id);
		if (sp->s_type == S_NEW) {
			if (sp->s_flag&S_GBL) {
				esp->e_flag = 1;
				esp->e_base.e_sp = sp;
				return;
			}
			err('u');
		} else {
			esp->e_mode = sp->s_type;
			esp->e_addr = sp->s_addr;
			esp->e_base.e_ap = sp->s_area;
		}
		return;
	}
	/*
	 * Else not a term.
	 */
	qerr();
}

/*)Function	int	digit(c, r)
 *
 *		int	c		digit character
 *		int	r		current radix
 *
 *	The function digit() returns the value of c
 *	in the current radix r.  If the c value is not
 *	a number of the current radix then a -1 is returned.
 *
 *	local variables:
 *		none
 *
 *	global variables:
 *		char	ctype[]		array of character types, one per
 *					ASCII character
 *
 *	functions called:
 *		none
 *
 *	side effects:
 *		none
 */

int
digit(c, r)
register int c, r;
{
	if (r == 16) {
		if (ctype[c] & RAD16) {
			if (c >= 'A' && c <= 'F')
				return (c - 'A' + 10);
			if (c >= 'a' && c <= 'f')
				return (c - 'a' + 10);
			return (c - '0');
		}
	} else
	if (r == 10) {
		if (ctype[c] & RAD10)
			return (c - '0');
	} else
	if (r == 8) {
		if (ctype[c] & RAD8)
			return (c - '0');
	} else
	if (r == 2) {
		if (ctype[c] & RAD2)
			return (c - '0');
	}
	return (-1);
}

/*)Function	VOID	abscheck(esp)
 *
 *		expr *	esp		pointer to an expr structure
 *
 *	The function abscheck() tests the evaluation of an
 *	expression to verify it is absolute.  If the evaluation
 *	is relocatable then an 'r' error is noted and the expression
 *	made absolute.
 *
 *	Note:	The area type (i.e. ABS) is not checked because
 *		the linker can be told to explicitly relocate an
 *		absolute area.
 *
 *	local variables:
 *		none
 *
 *	global variables:
 *		none
 *
 *	functions called:
 *		VOID	rerr()		assubr.c
 *
 *	side effects:
 *		The expression may be changed to absolute and the
 *		'r' error invoked.
 */

VOID
abscheck(esp)
register struct expr *esp;
{
	if (esp->e_flag || esp->e_base.e_ap) {
		esp->e_flag = 0;
		esp->e_base.e_ap = NULL;
		rerr();
	}
}

/*)Function	int	is_abs(esp)
 *
 *		expr *	esp		pointer to an expr structure
 *
 *	The function is_abs() tests the evaluation of an
 *	expression to verify it is absolute.  If the evaluation
 *	is absolute then 1 is returned, else 0 is returned.
 *
 *	Note:	The area type (i.e. ABS) is not checked because
 *		the linker can be told to explicitly relocate an
 *		absolute area.
 *
 *	local variables:
 *		none
 *
 *	global variables:
 *		none
 *
 *	functions called:
 *		none
 *
 *	side effects:
 *		none
 */

int
is_abs (esp)
register struct expr *esp;
{
	if (esp->e_flag || esp->e_base.e_ap) {
		return(0);
	}
	return(1);
}

/*)Function	int	oprio(c)
 *
 *		int	c		operator character
 *
 *	The function oprio() returns a relative priority
 *	for all valid unary and binary operators.
 *
 *	local variables:
 *		none
 *
 *	global variables:
 *		none
 *
 *	functions called:
 *		none
 *
 *	side effects:
 *		none
 */
 
int
oprio(c)
register int c;
{
	if (c == '*' || c == '/' || c == '%')
		return (10);
	if (c == '+' || c == '-')
		return (7);
	if (c == '<' || c == '>')
		return (5);
	if (c == '^')
		return (4);
	if (c == '&')
		return (3);
	if (c == '|')
		return (1);
	return (0);
}

/*)Function	VOID	clrexpr(esp)
 *
 *		expr *	esp		pointer to expression structure
 *
 *	The function clrexpr() clears the expression structure.
 *
 *	local variables:
 *		none
 *
 *	global variables:
 *		none
 *
 *	functions called:
 *		none
 *
 *	side effects:
 *		expression structure cleared.
 */
 
VOID
clrexpr(esp)
register struct expr *esp;
{
	esp->e_mode = 0;
	esp->e_flag = 0;
	esp->e_addr = 0;
	esp->e_base.e_ap = NULL;
	esp->e_rlcf = 0;
}
