gbdk-releases/sdcc/as/mcs51/asexpr.c
2015-01-10 16:25:09 +01:00

712 lines
18 KiB
C

/* asexpr.c */
/*
* (C) Copyright 1989-1995
* All Rights Reserved
*
* Alan R. Baldwin
* 721 Berkeley St.
* Kent, Ohio 44240
*/
#include <stdio.h>
#include <setjmp.h>
#include <string.h>
#include "asm.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.
*
* local variables:
* 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 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 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;
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;
esp->e_addr += re.e_addr;
} 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();
esp->e_addr -= re.e_addr;
} else {
/*
* Both operands (esp and re) must be constants
*/
/* SD :- moved the abscheck to each case
case and change the right shift operator.. if
right shift by 8 bits of a relocatable address then
the user wants the higher order byte. set the R_MSB
for the expression */
switch (c) {
case '*':
abscheck(esp);
abscheck(&re);
esp->e_addr *= re.e_addr;
break;
case '/':
abscheck(esp);
abscheck(&re);
esp->e_addr /= re.e_addr;
break;
case '&':
abscheck(esp);
abscheck(&re);
esp->e_addr &= re.e_addr;
break;
case '|':
abscheck(esp);
abscheck(&re);
esp->e_addr |= re.e_addr;
break;
case '%':
abscheck(esp);
abscheck(&re);
esp->e_addr %= re.e_addr;
break;
case '^':
abscheck(esp);
abscheck(&re);
esp->e_addr ^= re.e_addr;
break;
case '<':
abscheck(esp);
abscheck(&re);
esp->e_addr <<= re.e_addr;
break;
case '>':
/* SD change here */
abscheck(&re);
/* if the left is a relative address &
the right side is == 8 then */
if (esp->e_base.e_ap && re.e_addr == 8) {
esp->e_rlcf |= R_MSB ;
break;
}
else if (esp->e_base.e_ap && re.e_addr == 16)
{
if (flat24Mode)
{
esp->e_rlcf |= R_HIB;
}
else
{
warnBanner();
fprintf(stderr,
"(expr >> 16) is only meaningful in "
".flat24 mode.\n");
qerr();
}
break;
}
/* else continue with the normal processing */
abscheck(esp);
esp->e_addr >>= re.e_addr;
break;
default:
qerr();
break;
}
}
}
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.
*
* 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=0, v;
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 = 0-esp->e_addr;
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;
}
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[(int)*jp] & 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;
}
r = radix;
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;
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;
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) {
esp->e_addr = 0;
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;
}