mirror of http://shamusworld.gotdns.org/git/rmac
1143 lines
26 KiB
C
1143 lines
26 KiB
C
//
|
|
// RMAC - Renamed Macro Assembler for all Atari computers
|
|
// EXPR.C - Expression Analyzer
|
|
// Copyright (C) 199x Landon Dyer, 2011-2021 Reboot and Friends
|
|
// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
|
|
// Source utilised with the kind permission of Landon Dyer
|
|
//
|
|
|
|
#include "expr.h"
|
|
#include "direct.h"
|
|
#include "error.h"
|
|
#include "listing.h"
|
|
#include "mach.h"
|
|
#include "procln.h"
|
|
#include "riscasm.h"
|
|
#include "sect.h"
|
|
#include "symbol.h"
|
|
#include "token.h"
|
|
|
|
#define DEF_KW // Declare keyword values
|
|
#include "kwtab.h" // Incl generated keyword tables & defs
|
|
|
|
// N.B.: The size of tokenClass should be identical to the largest value of
|
|
// a token; we're assuming 256 but not 100% sure!
|
|
static char tokenClass[256]; // Generated table of token classes
|
|
static uint64_t evstk[EVSTACKSIZE]; // Evaluator value stack
|
|
static WORD evattr[EVSTACKSIZE]; // Evaluator attribute stack
|
|
|
|
// Token-class initialization list
|
|
char itokcl[] = {
|
|
0, // END
|
|
CONST, FCONST, SYMBOL, 0, // ID
|
|
'(', '[', '{', 0, // OPAR
|
|
')', ']', '}', 0, // CPAR
|
|
CR_DEFINED, CR_REFERENCED, // SUNARY (special unary)
|
|
CR_STREQ, CR_MACDEF,
|
|
CR_DATE, CR_TIME,
|
|
CR_ABSCOUNT, CR_FILESIZE, 0,
|
|
'!', '~', UNMINUS, UNLT, UNGT, 0, // UNARY
|
|
'*', '/', '%', 0, // MULT
|
|
'+', '-', 0, // ADD
|
|
SHL, SHR, 0, // SHIFT
|
|
LE, GE, '<', '>', NE, '=', 0, // REL
|
|
'&', 0, // AND
|
|
'^', 0, // XOR
|
|
'|', 0, // OR
|
|
1 // (the end)
|
|
};
|
|
|
|
const char missym_error[] = "missing symbol";
|
|
const char str_error[] = "missing symbol or string";
|
|
const char noflt_error[] = "operator not usable with float";
|
|
|
|
// Convert expression to postfix
|
|
static PTR evalTokenBuffer; // Deposit tokens here (this is really a
|
|
// pointer to exprbuf from direct.c)
|
|
// (Can also be from others, like
|
|
// riscasm.c)
|
|
static int symbolNum; // Pointer to the entry in symbolPtr[]
|
|
|
|
//
|
|
// Obtain a string value
|
|
//
|
|
static uint32_t str_value(char * p)
|
|
{
|
|
uint32_t v;
|
|
|
|
for(v=0; *p; p++)
|
|
v = (v << 8) | (*p & 0xFF);
|
|
|
|
return v;
|
|
}
|
|
|
|
//
|
|
// Initialize expression analyzer
|
|
//
|
|
void InitExpression(void)
|
|
{
|
|
// Initialize token-class table (all set to END)
|
|
for(int i=0; i<256; i++)
|
|
tokenClass[i] = END;
|
|
|
|
int i = 0;
|
|
|
|
for(char * p=itokcl; *p!=1; p++)
|
|
{
|
|
if (*p == 0)
|
|
i++;
|
|
else
|
|
tokenClass[(int)(*p)] = (char)i;
|
|
}
|
|
|
|
symbolNum = 0;
|
|
}
|
|
|
|
extern int correctMathRules;
|
|
int xor(void);
|
|
int and(void);
|
|
int rel(void);
|
|
int shift(void);
|
|
int sum(void);
|
|
int product(void);
|
|
|
|
//
|
|
// Binary operators (all the same precedence,
|
|
// except if -4 is passed to the command line)
|
|
//
|
|
#define precedence(HIERARCHY_HIGHER, HIERARCHY_CURRENT) \
|
|
do \
|
|
{ \
|
|
if (HIERARCHY_HIGHER() != OK) \
|
|
return ERROR; \
|
|
while (tokenClass[*tok] == HIERARCHY_CURRENT) \
|
|
{ \
|
|
TOKEN t = *tok++; \
|
|
if (HIERARCHY_HIGHER() != OK) \
|
|
return ERROR; \
|
|
*evalTokenBuffer.u32++ = t; \
|
|
} \
|
|
}while (0)
|
|
|
|
int expr0(void)
|
|
{
|
|
if ( correctMathRules == 0 )
|
|
{
|
|
if (expr1() != OK)
|
|
return ERROR;
|
|
|
|
while (tokenClass[*tok] >= MULT)
|
|
{
|
|
TOKEN t = *tok++;
|
|
|
|
if (expr1() != OK)
|
|
return ERROR;
|
|
|
|
*evalTokenBuffer.u32++ = t;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The order of C precedence (lower to higher):
|
|
// bitwise XOR ^
|
|
// bitwise OR |
|
|
// bitwise AND &
|
|
// relational = < <= >= > !=
|
|
// shifts << >>
|
|
// sum + -
|
|
// product * /
|
|
precedence(xor, OR);
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
int xor(void)
|
|
{
|
|
precedence(and, XOR);
|
|
return OK;
|
|
}
|
|
|
|
int and(void)
|
|
{
|
|
precedence(rel, AND);
|
|
return OK;
|
|
}
|
|
|
|
int rel(void)
|
|
{
|
|
precedence(shift, REL);
|
|
return OK;
|
|
}
|
|
|
|
int shift(void)
|
|
{
|
|
precedence(sum, SHIFT);
|
|
return OK;
|
|
}
|
|
|
|
int sum(void)
|
|
{
|
|
precedence(product, ADD);
|
|
return OK;
|
|
}
|
|
|
|
int product(void)
|
|
{
|
|
precedence(expr1, MULT);
|
|
return OK;
|
|
}
|
|
|
|
//
|
|
// Unary operators (detect unary '-')
|
|
// ggn: If expression starts with a plus then also eat it up. For some reason
|
|
// the parser gets confused when this happens and emits a "bad
|
|
// expression".
|
|
//
|
|
int expr1(void)
|
|
{
|
|
char * p;
|
|
WORD w;
|
|
|
|
int class = tokenClass[*tok];
|
|
|
|
if (*tok == '-' || *tok == '+' || *tok == '<' || *tok == '>' || class == UNARY)
|
|
{
|
|
TOKEN t = *tok++;
|
|
|
|
if (expr2() != OK)
|
|
return ERROR;
|
|
|
|
if (t == '-')
|
|
t = UNMINUS;
|
|
else if (t == '<')
|
|
t = UNLT;
|
|
else if (t == '>')
|
|
t = UNGT;
|
|
|
|
// With leading + we don't have to deposit anything to the buffer
|
|
// because there's no unary '+' nor we have to do anything about it
|
|
if (t != '+')
|
|
*evalTokenBuffer.u32++ = t;
|
|
}
|
|
else if (class == SUNARY)
|
|
{
|
|
switch (*tok++)
|
|
{
|
|
case CR_ABSCOUNT:
|
|
if (cursect != ABS)
|
|
{
|
|
*evalTokenBuffer.u32++ = CONST;
|
|
*evalTokenBuffer.u64++ = sect[ABS].sloc;
|
|
}
|
|
else
|
|
{
|
|
*evalTokenBuffer.u32++ = CONST;
|
|
*evalTokenBuffer.u64++ = sloc;
|
|
}
|
|
break;
|
|
case CR_FILESIZE:
|
|
if (*tok++ != STRING)
|
|
return error("^^FILESIZE expects filename inside string");
|
|
*evalTokenBuffer.u32++ = CONST;
|
|
// @@copypasted from d_incbin, maybe factor this out somehow?
|
|
// Attempt to open the include file in the current directory, then (if that
|
|
// failed) try list of include files passed in the enviroment string or by
|
|
// the "-d" option.
|
|
int fd, i;
|
|
char buf1[256];
|
|
|
|
if ((fd = open(string[*tok], _OPEN_INC)) < 0)
|
|
{
|
|
for(i=0; nthpath("RMACPATH", i, buf1)!=0; i++)
|
|
{
|
|
fd = strlen(buf1);
|
|
|
|
// Append path char if necessary
|
|
if ((fd > 0) && (buf1[fd - 1] != SLASHCHAR))
|
|
strcat(buf1, SLASHSTRING);
|
|
|
|
strcat(buf1, string[*tok]);
|
|
|
|
if ((fd = open(buf1, _OPEN_INC)) >= 0)
|
|
goto allright;
|
|
}
|
|
|
|
return error("cannot open: \"%s\"", string[*tok]);
|
|
}
|
|
|
|
allright:
|
|
*evalTokenBuffer.u64++ = (uint64_t)lseek(fd, 0L, SEEK_END);
|
|
close(fd);
|
|
|
|
// Advance tok because of consumed string token
|
|
tok++;
|
|
break;
|
|
case CR_TIME:
|
|
*evalTokenBuffer.u32++ = CONST;
|
|
*evalTokenBuffer.u64++ = dos_time();
|
|
break;
|
|
case CR_DATE:
|
|
*evalTokenBuffer.u32++ = CONST;
|
|
*evalTokenBuffer.u64++ = dos_date();
|
|
break;
|
|
case CR_MACDEF: // ^^macdef <macro-name>
|
|
if (*tok++ != SYMBOL)
|
|
return error(missym_error);
|
|
|
|
p = string[*tok++];
|
|
w = (lookup(p, MACRO, 0) == NULL ? 0 : 1);
|
|
*evalTokenBuffer.u32++ = CONST;
|
|
*evalTokenBuffer.u64++ = (uint64_t)w;
|
|
break;
|
|
case CR_DEFINED:
|
|
w = DEFINED;
|
|
goto getsym;
|
|
case CR_REFERENCED:
|
|
w = REFERENCED;
|
|
getsym:
|
|
if (*tok++ != SYMBOL)
|
|
return error(missym_error);
|
|
|
|
p = string[*tok++];
|
|
int j = (*p == '.' ? curenv : 0);
|
|
SYM * sy = lookup(p, LABEL, j);
|
|
w = ((sy != NULL) && (sy->sattr & w ? 1 : 0));
|
|
*evalTokenBuffer.u32++ = CONST;
|
|
*evalTokenBuffer.u64++ = (uint64_t)w;
|
|
break;
|
|
case CR_STREQ:
|
|
if (*tok != SYMBOL && *tok != STRING)
|
|
return error(str_error);
|
|
|
|
p = string[tok[1]];
|
|
tok +=2;
|
|
|
|
if (*tok++ != ',')
|
|
return error(comma_error);
|
|
|
|
if (*tok != SYMBOL && *tok != STRING)
|
|
return error(str_error);
|
|
|
|
char * p2 = string[tok[1]];
|
|
tok += 2;
|
|
|
|
w = (WORD)(!strcmp(p, p2));
|
|
*evalTokenBuffer.u32++ = CONST;
|
|
*evalTokenBuffer.u64++ = (uint64_t)w;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
return expr2();
|
|
|
|
return OK;
|
|
}
|
|
|
|
//
|
|
// Terminals (CONSTs) and parenthesis grouping
|
|
//
|
|
int expr2(void)
|
|
{
|
|
PTR ptk;
|
|
|
|
switch (*tok++)
|
|
{
|
|
case CONST:
|
|
ptk.u32 = tok;
|
|
*evalTokenBuffer.u32++ = CONST;
|
|
*evalTokenBuffer.u64++ = *ptk.u64++;
|
|
tok = ptk.u32;
|
|
break;
|
|
case FCONST:
|
|
ptk.u32 = tok;
|
|
*evalTokenBuffer.u32++ = FCONST;
|
|
*evalTokenBuffer.u64++ = *ptk.u64++;
|
|
tok = ptk.u32;
|
|
break;
|
|
case SYMBOL:
|
|
{
|
|
char * p = string[*tok++];
|
|
int j = (*p == '.' ? curenv : 0);
|
|
SYM * sy = lookup(p, LABEL, j);
|
|
|
|
if (sy == NULL)
|
|
sy = NewSymbol(p, LABEL, j);
|
|
|
|
*evalTokenBuffer.u32++ = SYMBOL;
|
|
*evalTokenBuffer.u32++ = symbolNum;
|
|
symbolPtr[symbolNum] = sy;
|
|
symbolNum++;
|
|
break;
|
|
}
|
|
case STRING:
|
|
*evalTokenBuffer.u32++ = CONST;
|
|
*evalTokenBuffer.u64++ = str_value(string[*tok++]);
|
|
break;
|
|
case '(':
|
|
if (expr0() != OK)
|
|
return ERROR;
|
|
|
|
if (*tok++ != ')')
|
|
return error("missing closing parenthesis ')'");
|
|
|
|
break;
|
|
case '[':
|
|
if (expr0() != OK)
|
|
return ERROR;
|
|
|
|
if (*tok++ != ']')
|
|
return error("missing closing bracket ']'");
|
|
|
|
break;
|
|
case '{':
|
|
if (expr0() != OK) // Eat up first parameter (register or immediate)
|
|
return ERROR;
|
|
|
|
if (*tok++ != ':') // Demand a ':' there
|
|
return error("missing colon ':'");
|
|
|
|
if (expr0() != OK) // Eat up second parameter (register or immediate)
|
|
return ERROR;
|
|
|
|
if (*tok++ != '}')
|
|
return error("missing closing brace '}'");
|
|
|
|
break;
|
|
case '$':
|
|
*evalTokenBuffer.u32++ = ACONST; // Attributed const
|
|
*evalTokenBuffer.u32++ = sloc; // Current location
|
|
*evalTokenBuffer.u32++ = DEFINED | ((orgactive | org68k_active) ? 0 : cursect); // Store attribs
|
|
break;
|
|
case '*':
|
|
*evalTokenBuffer.u32++ = ACONST; // Attributed const
|
|
|
|
// pcloc == location at start of line
|
|
*evalTokenBuffer.u32++ = (orgactive ? orgaddr : pcloc);
|
|
// '*' takes attributes of current section, not ABS!
|
|
// Also, if we're ORG'd, the symbol is absolute
|
|
*evalTokenBuffer.u32++ = DEFINED | ((orgactive | org68k_active) ? 0 : cursect);
|
|
break;
|
|
default:
|
|
return error("bad expression");
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
//
|
|
// Recursive-descent expression analyzer (with some simple speed hacks)
|
|
//
|
|
int expr(TOKEN * otk, uint64_t * a_value, WORD * a_attr, SYM ** a_esym)
|
|
{
|
|
// Passed in values (once derefenced, that is) can all be zero. They are
|
|
// there so that the expression analyzer can fill them in as needed. The
|
|
// expression analyzer gets its input from the global token pointer "tok",
|
|
// and not from anything passed in by the user.
|
|
SYM * symbol;
|
|
char * p;
|
|
int j;
|
|
PTR ptk;
|
|
|
|
evalTokenBuffer.u32 = otk; // Set token pointer to 'exprbuf' (direct.c)
|
|
// Also set in various other places too (riscasm.c,
|
|
// e.g.)
|
|
|
|
// Optimize for single constant or single symbol.
|
|
// Shamus: Seems to me that this could be greatly simplified by 1st
|
|
// checking if the first token is a multibyte token, *then*
|
|
// checking if there's an EOL after it depending on the actual
|
|
// length of the token (multiple vs. single). Otherwise, we have
|
|
// the horror show that is the following:
|
|
if ((tok[1] == EOL
|
|
&& (tok[0] != CONST && tokenClass[tok[0]] != SUNARY))
|
|
|| ((tok[0] == SYMBOL)
|
|
&& (tokenClass[tok[2]] < UNARY))
|
|
|| ((tok[0] == CONST) && (tokenClass[tok[3]] < UNARY))
|
|
)
|
|
// Shamus: Yes, you can parse that out and make some kind of sense of it, but damn, it takes a while to get it and understand the subtle bugs that result from not being careful about what you're checking; especially vis-a-vis naively checking tok[1] for an EOL. O_o
|
|
{
|
|
if (*tok == CONST)
|
|
{
|
|
ptk.u32 = tok;
|
|
*evalTokenBuffer.u32++ = *ptk.u32++;
|
|
*evalTokenBuffer.u64++ = *a_value = *ptk.u64++;
|
|
*a_attr = ABS | DEFINED;
|
|
tok = ptk.u32;
|
|
|
|
if (a_esym != NULL)
|
|
*a_esym = NULL;
|
|
|
|
//printf("Quick eval in expr(): CONST = %i, tokenClass[tok[2]] = %i\n", *a_value, tokenClass[*tok]);
|
|
}
|
|
// Not sure that removing float constant here is going to break anything and/or
|
|
// make things significantly slower, but having this here seems to cause the
|
|
// complexity of the check to get to this part of the parse to go through the
|
|
// roof, and dammit, I just don't feel like fighting that fight ATM. :-P
|
|
#if 0
|
|
else if (*tok == FCONST)
|
|
{
|
|
*evalTokenBuffer.u32++ = *tok++;
|
|
*evalTokenBuffer.u64++ = *a_value = *tok.u64++;
|
|
*a_attr = ABS | DEFINED | FLOAT;
|
|
|
|
if (a_esym != NULL)
|
|
*a_esym = NULL;
|
|
|
|
//printf("Quick eval in expr(): CONST = %i, tokenClass[tok[2]] = %i\n", *a_value, tokenClass[*tok]);
|
|
}
|
|
#endif
|
|
else if (*tok == '*')
|
|
{
|
|
*evalTokenBuffer.u32++ = CONST;
|
|
|
|
if (orgactive | org68k_active)
|
|
{
|
|
*evalTokenBuffer.u64++ = *a_value = orgaddr;
|
|
*a_attr = DEFINED; // We have ORG active, it doesn't belong in a section!
|
|
}
|
|
else
|
|
{
|
|
*evalTokenBuffer.u64++ = *a_value = pcloc;
|
|
// '*' takes attributes of current section, not ABS!
|
|
*a_attr = cursect | DEFINED;
|
|
}
|
|
|
|
|
|
if (a_esym != NULL)
|
|
*a_esym = NULL;
|
|
|
|
tok++;
|
|
}
|
|
else if (*tok == STRING || *tok == SYMBOL)
|
|
{
|
|
p = string[tok[1]];
|
|
j = (*p == '.' ? curenv : 0);
|
|
symbol = lookup(p, LABEL, j);
|
|
|
|
if (symbol == NULL)
|
|
symbol = NewSymbol(p, LABEL, j);
|
|
|
|
symbol->sattr |= REFERENCED;
|
|
|
|
// Check for undefined register equates, but only if it's not part
|
|
// of a #<SYMBOL> construct, as it could be that the label that's
|
|
// been undefined may later be used as an address label--which
|
|
// means it will be fixed up later, and thus, not an error.
|
|
if ((symbol->sattre & UNDEF_EQUR) && !riscImmTokenSeen)
|
|
{
|
|
error("undefined register equate '%s'", symbol->sname);
|
|
}
|
|
|
|
*evalTokenBuffer.u32++ = SYMBOL;
|
|
#if 0
|
|
*evalTokenBuffer++ = (TOKEN)symbol;
|
|
#else
|
|
/*
|
|
While this approach works, it's wasteful. It would be better to use something
|
|
that's already available, like the symbol "order defined" table (which needs to
|
|
be converted from a linked list into an array).
|
|
*/
|
|
*evalTokenBuffer.u32++ = symbolNum;
|
|
symbolPtr[symbolNum] = symbol;
|
|
symbolNum++;
|
|
#endif
|
|
|
|
*a_value = (symbol->sattr & DEFINED ? symbol->svalue : 0);
|
|
*a_attr = (WORD)(symbol->sattr & ~GLOBAL);
|
|
|
|
if (symbol->sattre & EQUATEDREG)
|
|
{
|
|
*a_attr |= RISCREG; // Mark it as a register, 'cause it is
|
|
*a_esym = symbol;
|
|
}
|
|
|
|
if ((symbol->sattr & (GLOBAL | DEFINED)) == GLOBAL
|
|
&& a_esym != NULL)
|
|
*a_esym = symbol;
|
|
|
|
tok += 2;
|
|
}
|
|
// Holy hell... This is likely due to the fact that LSR is mistakenly set as a SUNARY type... Need to fix this... !!! FIX !!!
|
|
else if (m6502)
|
|
{
|
|
*evalTokenBuffer.u32++ = *tok++;
|
|
}
|
|
else
|
|
{
|
|
// Unknown type here... Alert the user!,
|
|
error("undefined RISC register in expression [token=$%X]", *tok);
|
|
// Prevent spurious error reporting...
|
|
tok++;
|
|
return ERROR;
|
|
}
|
|
|
|
*evalTokenBuffer.u32++ = ENDEXPR;
|
|
return OK;
|
|
}
|
|
|
|
if (expr0() != OK)
|
|
return ERROR;
|
|
|
|
*evalTokenBuffer.u32++ = ENDEXPR;
|
|
return evexpr(otk, a_value, a_attr, a_esym);
|
|
}
|
|
|
|
//
|
|
// Evaluate expression.
|
|
// If the expression involves only ONE external symbol, the expression is
|
|
// UNDEFINED, but its value includes everything but the symbol value, and
|
|
// 'a_esym' is set to the external symbol.
|
|
//
|
|
int evexpr(TOKEN * _tk, uint64_t * a_value, WORD * a_attr, SYM ** a_esym)
|
|
{
|
|
WORD attr;
|
|
SYM * sy;
|
|
uint64_t * sval = evstk; // (Empty) initial stack
|
|
WORD * sattr = evattr;
|
|
SYM * esym = NULL; // No external symbol involved
|
|
WORD sym_seg = 0;
|
|
PTR tk;
|
|
tk.u32 = _tk;
|
|
|
|
while (*tk.u32 != ENDEXPR)
|
|
{
|
|
switch ((int)*tk.u32++)
|
|
{
|
|
case SYMBOL:
|
|
sy = symbolPtr[*tk.u32++];
|
|
sy->sattr |= REFERENCED; // Set "referenced" bit
|
|
|
|
if (!(sy->sattr & DEFINED))
|
|
{
|
|
// Reference to undefined symbol
|
|
if (!(sy->sattr & GLOBAL))
|
|
{
|
|
*a_attr = 0;
|
|
*a_value = 0;
|
|
return OK;
|
|
}
|
|
|
|
if (esym != NULL) // Check for multiple externals
|
|
return error(seg_error);
|
|
|
|
esym = sy;
|
|
}
|
|
|
|
if (sy->sattr & DEFINED)
|
|
*++sval = sy->svalue; // Push symbol's value
|
|
else
|
|
*++sval = 0; // 0 for undefined symbols
|
|
|
|
*++sattr = (WORD)(sy->sattr & ~GLOBAL); // Push attribs
|
|
sym_seg = (WORD)(sy->sattr & TDB);
|
|
break;
|
|
|
|
case CONST:
|
|
*++sval = *tk.u64++;
|
|
*++sattr = ABS | DEFINED; // Push simple attribs
|
|
break;
|
|
|
|
case FCONST:
|
|
// Even though it's a double, we can treat it like a uint64_t since
|
|
// we're just moving the bits around.
|
|
*++sval = *tk.u64++;
|
|
*++sattr = ABS | DEFINED | FLOAT; // Push simple attribs
|
|
break;
|
|
|
|
case ACONST:
|
|
*++sval = *tk.u32++; // Push value
|
|
*++sattr = (WORD)*tk.u32++; // Push attribs
|
|
break;
|
|
|
|
// Binary "+" and "-" matrix:
|
|
//
|
|
// ABS Sect Other
|
|
// ----------------------------
|
|
// ABS | ABS | Sect | Other |
|
|
// Sect | Sect | [1] | Error |
|
|
// Other | Other | Error | [1] |
|
|
// ----------------------------
|
|
//
|
|
// [1] + : Error
|
|
// - : ABS
|
|
|
|
case '+':
|
|
--sval; // Pop value
|
|
--sattr; // Pop attrib
|
|
// Get FLOAT attribute, if any
|
|
attr = (sattr[0] | sattr[1]) & FLOAT;
|
|
|
|
// Since adding an int to a fp value promotes it to a fp value, we
|
|
// don't care whether it's first or second; we cast to to a double
|
|
// regardless.
|
|
if (attr == FLOAT)
|
|
{
|
|
PTR p;
|
|
p.u64 = sval;
|
|
double fpval1 = (sattr[0] & FLOAT ? *p.dp : (double)*p.i64);
|
|
p.u64++;
|
|
double fpval2 = (sattr[1] & FLOAT ? *p.dp : (double)*p.i64);
|
|
*(double *)sval = fpval1 + fpval2;
|
|
}
|
|
else
|
|
{
|
|
*sval += sval[1]; // Compute value
|
|
}
|
|
|
|
if (!(*sattr & TDB))
|
|
*sattr = sattr[1] | attr;
|
|
else if (sattr[1] & TDB)
|
|
return error(seg_error);
|
|
|
|
break;
|
|
|
|
case '-':
|
|
--sval; // Pop value
|
|
--sattr; // Pop attrib
|
|
// Get FLOAT attribute, if any
|
|
attr = (sattr[0] | sattr[1]) & FLOAT;
|
|
|
|
// Since subtracting an int to a fp value promotes it to a fp
|
|
// value, we don't care whether it's first or second; we cast to to
|
|
// a double regardless.
|
|
if (attr == FLOAT)
|
|
{
|
|
PTR p;
|
|
p.u64 = sval;
|
|
double fpval1 = (sattr[0] & FLOAT ? *p.dp : (double)*p.i64);
|
|
p.u64++;
|
|
double fpval2 = (sattr[1] & FLOAT ? *p.dp : (double)*p.i64);
|
|
*(double *)sval = fpval1 - fpval2;
|
|
}
|
|
else
|
|
{
|
|
*sval -= sval[1];
|
|
}
|
|
|
|
*sattr |= attr; // Inherit FLOAT attribute
|
|
attr = (WORD)(*sattr & TDB);
|
|
// If symbol1 is ABS, take attributes from symbol2
|
|
if (!attr)
|
|
*sattr = sattr[1];
|
|
// Otherwise, they're both TDB and so attributes cancel out
|
|
else if (sattr[1] & TDB)
|
|
*sattr &= ~TDB;
|
|
|
|
break;
|
|
|
|
// Unary operators only work on ABS items
|
|
case UNMINUS:
|
|
if (*sattr & TDB)
|
|
return error(seg_error);
|
|
|
|
if (*sattr & FLOAT)
|
|
{
|
|
double * dst = (double *)sval;
|
|
*dst = -*dst;
|
|
*sattr = ABS | DEFINED | FLOAT; // Expr becomes absolute
|
|
}
|
|
else
|
|
{
|
|
*sval = -(int64_t)*sval;
|
|
*sattr = ABS | DEFINED; // Expr becomes absolute
|
|
}
|
|
|
|
break;
|
|
|
|
case UNLT: // Unary < (get the low byte of a word)
|
|
if (*sattr & TDB)
|
|
return error(seg_error);
|
|
|
|
if (*sattr & FLOAT)
|
|
return error(noflt_error);
|
|
|
|
*sval = (int64_t)((*sval) & 0x00FF);
|
|
*sattr = ABS | DEFINED; // Expr becomes absolute
|
|
break;
|
|
|
|
case UNGT: // Unary > (get the high byte of a word)
|
|
if (*sattr & TDB)
|
|
return error(seg_error);
|
|
|
|
if (*sattr & FLOAT)
|
|
return error(noflt_error);
|
|
|
|
*sval = (int64_t)(((*sval) >> 8) & 0x00FF);
|
|
*sattr = ABS | DEFINED; // Expr becomes absolute
|
|
break;
|
|
|
|
case '!':
|
|
if (*sattr & TDB)
|
|
return error(seg_error);
|
|
|
|
if (*sattr & FLOAT)
|
|
return error("floating point numbers not allowed with operator '!'.");
|
|
|
|
*sval = !*sval;
|
|
*sattr = ABS | DEFINED; // Expr becomes absolute
|
|
break;
|
|
|
|
case '~':
|
|
if (*sattr & TDB)
|
|
return error(seg_error);
|
|
|
|
if (*sattr & FLOAT)
|
|
return error("floating point numbers not allowed with operator '~'.");
|
|
|
|
*sval = ~*sval;
|
|
*sattr = ABS | DEFINED; // Expr becomes absolute
|
|
break;
|
|
|
|
// Comparison operators must have two values that
|
|
// are in the same segment, but that's the only requirement.
|
|
case LE:
|
|
sattr--;
|
|
sval--;
|
|
|
|
if ((*sattr & TDB) != (sattr[1] & TDB))
|
|
return error(seg_error);
|
|
|
|
// Get FLOAT attribute, if any
|
|
attr = (sattr[0] | sattr[1]) & FLOAT;
|
|
|
|
// Cast any ints in the comparison to double, if there's at least
|
|
// one double in the comparison.
|
|
if (attr == FLOAT)
|
|
{
|
|
PTR p;
|
|
p.u64 = sval;
|
|
double fpval1 = (sattr[0] & FLOAT ? *p.dp : (double)*p.i64);
|
|
p.u64++;
|
|
double fpval2 = (sattr[1] & FLOAT ? *p.dp : (double)*p.i64);
|
|
*sval = (fpval1 <= fpval2);
|
|
}
|
|
else
|
|
{
|
|
*sval = (*sval <= sval[1]);
|
|
}
|
|
|
|
*sattr = ABS | DEFINED;
|
|
break;
|
|
|
|
case GE:
|
|
sattr--;
|
|
sval--;
|
|
|
|
if ((*sattr & TDB) != (sattr[1] & TDB))
|
|
return error(seg_error);
|
|
|
|
// Get FLOAT attribute, if any
|
|
attr = (sattr[0] | sattr[1]) & FLOAT;
|
|
|
|
// Cast any ints in the comparison to double, if there's at least
|
|
// one double in the comparison.
|
|
if (attr == FLOAT)
|
|
{
|
|
PTR p;
|
|
p.u64 = sval;
|
|
double fpval1 = (sattr[0] & FLOAT ? *p.dp : (double)*p.i64);
|
|
p.u64++;
|
|
double fpval2 = (sattr[1] & FLOAT ? *p.dp : (double)*p.i64);
|
|
*sval = (fpval1 >= fpval2);
|
|
}
|
|
else
|
|
{
|
|
*sval = (*sval >= sval[1]);
|
|
}
|
|
|
|
*sattr = ABS | DEFINED;
|
|
break;
|
|
|
|
case '>':
|
|
sattr--;
|
|
sval--;
|
|
|
|
if ((*sattr & TDB) != (sattr[1] & TDB))
|
|
return error(seg_error);
|
|
|
|
// Get FLOAT attribute, if any
|
|
attr = (sattr[0] | sattr[1]) & FLOAT;
|
|
|
|
// Cast any ints in the comparison to double, if there's at least
|
|
// one double in the comparison.
|
|
if (attr == FLOAT)
|
|
{
|
|
PTR p;
|
|
p.u64 = sval;
|
|
double fpval1 = (sattr[0] & FLOAT ? *p.dp : (double)*p.i64);
|
|
p.u64++;
|
|
double fpval2 = (sattr[1] & FLOAT ? *p.dp : (double)*p.i64);
|
|
*sval = (fpval1 > fpval2);
|
|
}
|
|
else
|
|
{
|
|
*sval = (*sval > sval[1]);
|
|
}
|
|
|
|
*sattr = ABS | DEFINED;
|
|
break;
|
|
|
|
case '<':
|
|
sattr--;
|
|
sval--;
|
|
|
|
if ((*sattr & TDB) != (sattr[1] & TDB))
|
|
return error(seg_error);
|
|
|
|
// Get FLOAT attribute, if any
|
|
attr = (sattr[0] | sattr[1]) & FLOAT;
|
|
|
|
// Cast any ints in the comparison to double, if there's at least
|
|
// one double in the comparison.
|
|
if (attr == FLOAT)
|
|
{
|
|
PTR p;
|
|
p.u64 = sval;
|
|
double fpval1 = (sattr[0] & FLOAT ? *p.dp : (double)*p.i64);
|
|
p.u64++;
|
|
double fpval2 = (sattr[1] & FLOAT ? *p.dp : (double)*p.i64);
|
|
*sval = (fpval1 < fpval2);
|
|
}
|
|
else
|
|
{
|
|
*sval = (*sval < sval[1]);
|
|
}
|
|
|
|
*sattr = ABS | DEFINED; // Expr forced to ABS
|
|
break;
|
|
|
|
case NE:
|
|
sattr--;
|
|
sval--;
|
|
|
|
if ((*sattr & TDB) != (sattr[1] & TDB))
|
|
return error(seg_error);
|
|
|
|
// Get FLOAT attribute, if any
|
|
attr = (sattr[0] | sattr[1]) & FLOAT;
|
|
|
|
// Cast any ints in the comparison to double, if there's at least
|
|
// one double in the comparison.
|
|
if (attr == FLOAT)
|
|
{
|
|
PTR p;
|
|
p.u64 = sval;
|
|
double fpval1 = (sattr[0] & FLOAT ? *p.dp : (double)*p.i64);
|
|
p.u64++;
|
|
double fpval2 = (sattr[1] & FLOAT ? *p.dp : (double)*p.i64);
|
|
*sval = (fpval1 != fpval2);
|
|
}
|
|
else
|
|
{
|
|
*sval = (*sval != sval[1]);
|
|
}
|
|
|
|
*sattr = ABS | DEFINED; // Expr forced to ABS
|
|
break;
|
|
|
|
case '=':
|
|
sattr--;
|
|
sval--;
|
|
|
|
if ((*sattr & TDB) != (sattr[1] & TDB))
|
|
return error(seg_error);
|
|
|
|
// Get FLOAT attribute, if any
|
|
attr = (sattr[0] | sattr[1]) & FLOAT;
|
|
|
|
// Cast any ints in the comparison to double, if there's at least
|
|
// one double in the comparison.
|
|
if (attr == FLOAT)
|
|
{
|
|
PTR p;
|
|
p.u64 = sval;
|
|
double fpval1 = (sattr[0] & FLOAT ? *p.dp : (double)*p.i64);
|
|
p.u64++;
|
|
double fpval2 = (sattr[1] & FLOAT ? *p.dp : (double)*p.i64);
|
|
*sval = (fpval1 == fpval2);
|
|
}
|
|
else
|
|
{
|
|
*sval = (*sval == sval[1]);
|
|
}
|
|
|
|
*sattr = ABS | DEFINED; // Expr forced to ABS
|
|
|
|
break;
|
|
|
|
// All other binary operators must have two ABS items to work with.
|
|
// They all produce an ABS value.
|
|
// Shamus: Is this true? There's at least one counterexample of legit
|
|
// code where this assumption fails to produce correct code.
|
|
default:
|
|
|
|
switch ((int)tk.u32[-1])
|
|
{
|
|
case '*':
|
|
sval--;
|
|
sattr--;
|
|
// Get FLOAT attribute, if any
|
|
attr = (sattr[0] | sattr[1]) & FLOAT;
|
|
|
|
// Since multiplying an int to a fp value promotes it to a fp
|
|
// value, we don't care whether it's first or second; it will
|
|
// be cast to a double regardless.
|
|
/*
|
|
An open question here is do we promote ints to floats as signed or unsigned? It makes a difference if, say, the int is put in as -1 but is promoted to a double as $FFFFFFFFFFFFFFFF--you get very different results that way! For now, we promote as signed until proven detrimental otherwise.
|
|
*/
|
|
if (attr == FLOAT)
|
|
{
|
|
PTR p;
|
|
p.u64 = sval;
|
|
double fpval1 = (sattr[0] & FLOAT ? *p.dp : (double)*p.i64);
|
|
p.u64++;
|
|
double fpval2 = (sattr[1] & FLOAT ? *p.dp : (double)*p.i64);
|
|
*(double *)sval = fpval1 * fpval2;
|
|
}
|
|
else
|
|
{
|
|
*sval *= sval[1];
|
|
}
|
|
|
|
break;
|
|
|
|
case '/':
|
|
sval--;
|
|
sattr--;
|
|
// Get FLOAT attribute, if any
|
|
attr = (sattr[0] | sattr[1]) & FLOAT;
|
|
|
|
if (attr == FLOAT)
|
|
{
|
|
PTR p;
|
|
p.u64 = sval;
|
|
double fpval1 = (sattr[0] & FLOAT ? *p.dp : (double)*p.i64);
|
|
p.u64++;
|
|
double fpval2 = (sattr[1] & FLOAT ? *p.dp : (double)*p.i64);
|
|
|
|
if (fpval2 == 0)
|
|
return error("divide by zero");
|
|
|
|
*(double *)sval = fpval1 / fpval2;
|
|
}
|
|
else
|
|
{
|
|
if (sval[1] == 0)
|
|
return error("divide by zero");
|
|
|
|
// Compiler is picky here: Without casting these, it
|
|
// discards the sign if dividing a negative # by a
|
|
// positive one, creating a bad result. :-/
|
|
// Definitely a side effect of using uint32_ts intead of
|
|
// ints.
|
|
*sval = (int32_t)sval[0] / (int32_t)sval[1];
|
|
}
|
|
|
|
break;
|
|
|
|
case '%':
|
|
sval--;
|
|
sattr--;
|
|
|
|
if ((*sattr | sattr[1]) & FLOAT)
|
|
return error("floating point numbers not allowed with operator '%'.");
|
|
|
|
if (sval[1] == 0)
|
|
return error("mod (%) by zero");
|
|
|
|
*sval %= sval[1];
|
|
break;
|
|
|
|
case SHL:
|
|
sval--;
|
|
sattr--; // Pop attrib
|
|
|
|
if ((*sattr | sattr[1]) & FLOAT)
|
|
return error("floating point numbers not allowed with operator '<<'.");
|
|
|
|
*sval <<= sval[1];
|
|
break;
|
|
|
|
case SHR:
|
|
sval--;
|
|
sattr--; // Pop attrib
|
|
|
|
if ((*sattr | sattr[1]) & FLOAT)
|
|
return error("floating point numbers not allowed with operator '>>'.");
|
|
|
|
*sval >>= sval[1];
|
|
break;
|
|
|
|
case '&':
|
|
sval--;
|
|
sattr--; // Pop attrib
|
|
|
|
if ((*sattr | sattr[1]) & FLOAT)
|
|
return error("floating point numbers not allowed with operator '&'.");
|
|
|
|
*sval &= sval[1];
|
|
break;
|
|
|
|
case '^':
|
|
sval--;
|
|
sattr--; // Pop attrib
|
|
|
|
if ((*sattr | sattr[1]) & FLOAT)
|
|
return error("floating point numbers not allowed with operator '^'.");
|
|
|
|
*sval ^= sval[1];
|
|
break;
|
|
|
|
case '|':
|
|
sval--;
|
|
sattr--;
|
|
|
|
if ((*sattr | sattr[1]) & FLOAT)
|
|
return error("floating point numbers not allowed with operator '|'.");
|
|
|
|
*sval |= sval[1];
|
|
break;
|
|
|
|
default:
|
|
// Bad operator in expression stream (this should never happen!)
|
|
interror(5);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (esym != NULL)
|
|
*sattr &= ~DEFINED;
|
|
|
|
if (a_esym != NULL)
|
|
*a_esym = esym;
|
|
|
|
// Copy value + attrib into return variables
|
|
*a_value = *sval;
|
|
*a_attr = *sattr;
|
|
|
|
return OK;
|
|
}
|
|
|
|
//
|
|
// Count the # of tokens in the passed in expression
|
|
// N.B.: 64-bit constants count as two tokens each
|
|
//
|
|
uint16_t ExpressionLength(TOKEN * tk)
|
|
{
|
|
uint16_t length;
|
|
|
|
for(length=0; tk[length]!=ENDEXPR; length++)
|
|
{
|
|
// Add one to length for 2X tokens, two for 3X tokens
|
|
if (tk[length] == SYMBOL)
|
|
length++;
|
|
else if ((tk[length] == CONST) || (tk[length] == FCONST))
|
|
length += 2;
|
|
}
|
|
|
|
// Add 1 for ENDEXPR
|
|
length++;
|
|
|
|
return length;
|
|
}
|