//
-// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System
+// RMAC - Reboot's Macro Assembler for all Atari computers
// EXPR.C - Expression Analyzer
-// Copyright (C) 199x Landon Dyer, 2017 Reboot and Friends
+// Copyright (C) 199x Landon Dyer, 2011-2020 Reboot and Friends
// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
// Source utilised with the kind permission of Landon Dyer
//
// 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 VALUE evstk[EVSTACKSIZE]; // Evaluator value stack
+static uint64_t evstk[EVSTACKSIZE]; // Evaluator value stack
static WORD evattr[EVSTACKSIZE]; // Evaluator attribute stack
// Token-class initialization list
char itokcl[] = {
0, // END
- CONST, SYMBOL, 0, // ID
+ 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, 0,
- '!', '~', UNMINUS, 0, // UNARY
+ CR_ABSCOUNT, CR_FILESIZE, 0,
+ '!', '~', UNMINUS, UNLT, UNGT, 0, // UNARY
'*', '/', '%', 0, // MULT
'+', '-', 0, // ADD
SHL, SHR, 0, // SHIFT
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 TOKEN * 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[]
+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 VALUE str_value(char * p)
+static uint32_t str_value(char * p)
{
- VALUE v;
+ uint32_t v;
for(v=0; *p; p++)
v = (v << 8) | (*p & 0xFF);
//
void InitExpression(void)
{
- int i;
- char * p;
-
// Initialize token-class table (all set to END)
- for(i=0; i<256; i++)
+ for(int i=0; i<256; i++)
tokenClass[i] = END;
- for(i=0, p=itokcl; *p!=1; p++)
+ int i = 0;
+
+ for(char * p=itokcl; *p!=1; p++)
{
if (*p == 0)
i++;
//
int expr0(void)
{
- TOKEN t;
-
if (expr1() != OK)
return ERROR;
while (tokenClass[*tok] >= MULT)
{
- t = *tok++;
+ TOKEN t = *tok++;
if (expr1() != OK)
return ERROR;
- *evalTokenBuffer++ = t;
+ *evalTokenBuffer.u32++ = t;
}
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".
+// 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)
{
- int class;
- TOKEN t;
- SYM * sy;
- char * p, * p2;
+ char * p;
WORD w;
- int j;
- class = tokenClass[*tok];
+ int class = tokenClass[*tok];
- if (*tok == '-' || *tok == '+' || class == UNARY)
+ if (*tok == '-' || *tok == '+' || *tok == '<' || *tok == '>' || class == UNARY)
{
- t = *tok++;
+ TOKEN t = *tok++;
if (expr2() != OK)
return ERROR;
if (t == '-')
t = UNMINUS;
-
- *evalTokenBuffer++ = t;
+ 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 ((int)*tok++)
+ switch (*tok++)
{
case CR_ABSCOUNT:
- *evalTokenBuffer++ = CONST;
- *evalTokenBuffer++ = (LONG)sect[ABS].sloc;
+ 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[1]]);
+ }
+
+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++ = CONST;
- *evalTokenBuffer++ = dos_time();
+ *evalTokenBuffer.u32++ = CONST;
+ *evalTokenBuffer.u64++ = dos_time();
break;
case CR_DATE:
- *evalTokenBuffer++ = CONST;
- *evalTokenBuffer++ = dos_date();
+ *evalTokenBuffer.u32++ = CONST;
+ *evalTokenBuffer.u64++ = dos_date();
break;
- case CR_MACDEF: // ^^macdef <macro-name>
+ 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++ = CONST;
- *evalTokenBuffer++ = (TOKEN)w;
+ *evalTokenBuffer.u32++ = CONST;
+ *evalTokenBuffer.u64++ = (uint64_t)w;
break;
case CR_DEFINED:
w = DEFINED;
return error(missym_error);
p = string[*tok++];
- j = (*p == '.' ? curenv : 0);
- w = ((sy = lookup(p, LABEL, j)) != NULL && (sy->sattr & w) ? 1 : 0);
- *evalTokenBuffer++ = CONST;
- *evalTokenBuffer++ = (TOKEN)w;
+ 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)
if (*tok != SYMBOL && *tok != STRING)
return error(str_error);
- p2 = string[tok[1]];
+ char * p2 = string[tok[1]];
tok += 2;
w = (WORD)(!strcmp(p, p2));
- *evalTokenBuffer++ = CONST;
- *evalTokenBuffer++ = (TOKEN)w;
+ *evalTokenBuffer.u32++ = CONST;
+ *evalTokenBuffer.u64++ = (uint64_t)w;
break;
}
}
//
int expr2(void)
{
- char * p;
- SYM * sy;
- int j;
+ PTR ptk;
- switch ((int)*tok++)
+ switch (*tok++)
{
case CONST:
- *evalTokenBuffer++ = CONST;
- *evalTokenBuffer++ = *tok++;
+ 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:
- p = string[*tok++];
- j = (*p == '.' ? curenv : 0);
- sy = lookup(p, LABEL, j);
+ {
+ char * p = string[*tok++];
+ int j = (*p == '.' ? curenv : 0);
+ SYM * sy = lookup(p, LABEL, j);
if (sy == NULL)
sy = NewSymbol(p, LABEL, j);
if (sy->sattre & EQUATEDREG)
{
if ((regbank == BANK_0) && (sy->sattre & BANK_1) && !altbankok)
- warns("equated symbol \'%s\' cannot be used in register bank 0", sy->sname);
+ warn("equated symbol \'%s\' cannot be used in register bank 0", sy->sname);
if ((regbank == BANK_1) && (sy->sattre & BANK_0) && !altbankok)
- warns("equated symbol \'%s\' cannot be used in register bank 1", sy->sname);
+ warn("equated symbol \'%s\' cannot be used in register bank 1", sy->sname);
}
- *evalTokenBuffer++ = SYMBOL;
- *evalTokenBuffer++ = symbolNum;
+ *evalTokenBuffer.u32++ = SYMBOL;
+ *evalTokenBuffer.u32++ = symbolNum;
symbolPtr[symbolNum] = sy;
symbolNum++;
break;
+ }
case STRING:
- *evalTokenBuffer++ = CONST;
- *evalTokenBuffer++ = str_value(string[*tok++]);
+ *evalTokenBuffer.u32++ = CONST;
+ *evalTokenBuffer.u64++ = str_value(string[*tok++]);
break;
case '(':
if (expr0() != OK)
return ERROR;
if (*tok++ != ')')
- return error("missing close parenthesis ')'");
+ return error("missing closing parenthesis ')'");
break;
case '[':
return ERROR;
if (*tok++ != ']')
- return error("missing close parenthesis ']'");
+ 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++ = ACONST; // Attributed const
- *evalTokenBuffer++ = sloc; // Current location
- *evalTokenBuffer++ = cursect | DEFINED; // Store attribs
+ *evalTokenBuffer.u32++ = ACONST; // Attributed const
+ *evalTokenBuffer.u32++ = sloc; // Current location
+ *evalTokenBuffer.u32++ = cursect | DEFINED; // Store attribs
break;
case '*':
- *evalTokenBuffer++ = ACONST; // Attributed const
+ *evalTokenBuffer.u32++ = ACONST; // Attributed const
// pcloc == location at start of line
- *evalTokenBuffer++ = (orgactive ? orgaddr : pcloc);
+ *evalTokenBuffer.u32++ = (orgactive ? orgaddr : pcloc);
// '*' takes attributes of current section, not ABS!
- *evalTokenBuffer++ = cursect | DEFINED;
+ *evalTokenBuffer.u32++ = cursect | DEFINED;
break;
default:
return error("bad expression");
//
// Recursive-descent expression analyzer (with some simple speed hacks)
//
-int expr(TOKEN * otk, VALUE * a_value, WORD * a_attr, SYM ** a_esym)
+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
SYM * symbol;
char * p;
int j;
+ PTR ptk;
- evalTokenBuffer = otk; // Set token pointer to 'exprbuf' (direct.c)
- // Also set in various other places too (riscasm.c, e.g.)
+ evalTokenBuffer.u32 = otk; // Set token pointer to 'exprbuf' (direct.c)
+ // Also set in various other places too (riscasm.c,
+ // e.g.)
//printf("expr(): tokens 0-2: %i %i %i (%c %c %c); tc[2] = %i\n", tok[0], tok[1], tok[2], tok[0], tok[1], tok[2], tokenClass[tok[2]]);
// Optimize for single constant or single symbol.
// This is probably a really bad assumption to be making here...!
// (assuming tok[1] == EOL is a single token that is)
// Seems that even other tokens (SUNARY type) can fuck this up too.
+#if 0
// if ((tok[1] == EOL)
- if ((tok[1] == EOL && (tok[0] != CONST && tokenClass[tok[0]] != SUNARY))
- || (((*tok == CONST || *tok == SYMBOL) || (*tok >= KW_R0 && *tok <= KW_R31))
- && (tokenClass[tok[2]] < UNARY)))
+ if ((tok[1] == EOL && ((tok[0] != CONST || tok[0] != FCONST) && tokenClass[tok[0]] != SUNARY))
+// || (((*tok == CONST || *tok == FCONST || *tok == SYMBOL) || (*tok >= KW_R0 && *tok <= KW_R31))
+// && (tokenClass[tok[2]] < UNARY)))
+ || (((tok[0] == SYMBOL) || (tok[0] >= KW_R0 && tok[0] <= KW_R31))
+ && (tokenClass[tok[2]] < UNARY))
+ || ((tok[0] == CONST || tok[0] == FCONST) && (tokenClass[tok[3]] < UNARY))
+ )
+#else
+// 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)
+ || (tok[0] >= KW_R0 && tok[0] <= KW_R31))
+ && (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 niavely checking tok[1] for an EOL. O_o
+#endif
{
if (*tok >= KW_R0 && *tok <= KW_R31)
{
- *evalTokenBuffer++ = CONST;
- *evalTokenBuffer++ = *a_value = (*tok - KW_R0);
+ *evalTokenBuffer.u32++ = CONST;
+ *evalTokenBuffer.u64++ = *a_value = (*tok - KW_R0);
*a_attr = ABS | DEFINED;
if (a_esym != NULL)
}
else if (*tok == CONST)
{
- *evalTokenBuffer++ = CONST;
- *evalTokenBuffer++ = *a_value = tok[1];
+ 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;
- tok += 2;
//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++ = CONST;
+ *evalTokenBuffer.u32++ = CONST;
if (orgactive)
- *evalTokenBuffer++ = *a_value = orgaddr;
+ *evalTokenBuffer.u64++ = *a_value = orgaddr;
else
- *evalTokenBuffer++ = *a_value = pcloc;
+ *evalTokenBuffer.u64++ = *a_value = pcloc;
// '*' takes attributes of current section, not ABS!
*a_attr = cursect | DEFINED;
// means it will be fixed up later, and thus, not an error.
if ((symbol->sattre & UNDEF_EQUR) && !riscImmTokenSeen)
{
- errors("undefined register equate '%s'", symbol->sname);
+ error("undefined register equate '%s'", symbol->sname);
//if we return right away, it returns some spurious errors...
// return ERROR;
}
if (symbol->sattre & EQUATEDREG)
{
if ((regbank == BANK_0) && (symbol->sattre & BANK_1) && !altbankok)
- warns("equated symbol '%s' cannot be used in register bank 0", symbol->sname);
+ warn("equated symbol '%s' cannot be used in register bank 0", symbol->sname);
if ((regbank == BANK_1) && (symbol->sattre & BANK_0) && !altbankok)
- warns("equated symbol '%s' cannot be used in register bank 1", symbol->sname);
+ warn("equated symbol '%s' cannot be used in register bank 1", symbol->sname);
}
- *evalTokenBuffer++ = SYMBOL;
+ *evalTokenBuffer.u32++ = SYMBOL;
#if 0
*evalTokenBuffer++ = (TOKEN)symbol;
#else
that's already available, like the symbol "order defined" table (which needs to
be converted from a linked list into an array).
*/
- *evalTokenBuffer++ = symbolNum;
+ *evalTokenBuffer.u32++ = symbolNum;
symbolPtr[symbolNum] = symbol;
symbolNum++;
#endif
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");
+ error("undefined RISC register in expression [token=$%X]", *tok);
// Prevent spurious error reporting...
tok++;
return ERROR;
}
- *evalTokenBuffer++ = ENDEXPR;
+ *evalTokenBuffer.u32++ = ENDEXPR;
return OK;
}
if (expr0() != OK)
return ERROR;
- *evalTokenBuffer++ = ENDEXPR;
+ *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 it's value includes everything but the symbol value, and
-// `a_esym' is set to the external symbol.
+// 'a_esym' is set to the external symbol.
//
-int evexpr(TOKEN * tk, VALUE * a_value, WORD * a_attr, SYM ** a_esym)
+int evexpr(TOKEN * _tk, uint64_t * a_value, WORD * a_attr, SYM ** a_esym)
{
- WORD * sattr;
- VALUE * sval;
WORD attr;
SYM * sy;
- SYM * esym;
- WORD sym_seg;
-
- sval = evstk; // (Empty) initial stack
- sattr = evattr;
- esym = NULL; // No external symbol involved
- sym_seg = 0;
-
- while (*tk != ENDEXPR)
+ 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++)
+ switch ((int)*tk.u32++)
{
case SYMBOL:
//printf("evexpr(): SYMBOL\n");
- sy = symbolPtr[*tk++];
+ sy = symbolPtr[*tk.u32++];
sy->sattr |= REFERENCED; // Set "referenced" bit
if (!(sy->sattr & DEFINED))
}
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:
-//printf("evexpr(): CONST = %i\n", *tk);
- *++sval = *tk++; // Push value
+ *++sval = *tk.u64++;
+//printf("evexpr(): CONST = %lX\n", *sval);
*++sattr = ABS | DEFINED; // Push simple attribs
break;
+
+ case FCONST:
+//printf("evexpr(): FCONST = %lf\n", *tk.dp);
+ // 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:
-//printf("evexpr(): ACONST = %i\n", *tk);
- *++sval = *tk++; // Push value
- *++sattr = (WORD)*tk++; // Push attribs
+//printf("evexpr(): ACONST = %i\n", *tk.u32);
+ *++sval = *tk.u32++; // Push value
+ *++sattr = (WORD)*tk.u32++; // Push attribs
break;
// Binary "+" and "-" matrix:
//
// [1] + : Error
// - : ABS
+
case '+':
//printf("evexpr(): +\n");
--sval; // Pop value
--sattr; // Pop attrib
//printf("--> N+N: %i + %i = ", *sval, sval[1]);
- *sval += sval[1]; // Compute value
+ // 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
+ }
//printf("%i\n", *sval);
if (!(*sattr & TDB))
- *sattr = sattr[1];
+ *sattr = sattr[1] | attr;
else if (sattr[1] & TDB)
return error(seg_error);
break;
+
case '-':
//printf("evexpr(): -\n");
--sval; // Pop value
--sattr; // Pop attrib
//printf("--> N-N: %i - %i = ", *sval, sval[1]);
- *sval -= sval[1]; // Compute value
+ // 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];
+ }
//printf("%i\n", *sval);
+ *sattr |= attr; // Inherit FLOAT attribute
attr = (WORD)(*sattr & TDB);
#if 0
printf("EVEXPR (-): sym1 = %X, sym2 = %X\n", attr, sattr[1]);
*sattr &= ~TDB;
break;
+
// Unary operators only work on ABS items
case UNMINUS:
//printf("evexpr(): UNMINUS\n");
if (*sattr & TDB)
- error(seg_error);
+ 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)
+//printf("evexpr(): UNLT\n");
+ if (*sattr & TDB)
+ return error(seg_error);
- *sval = -(int)*sval;
+ 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)
+//printf("evexpr(): UNGT\n");
+ 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 '!':
//printf("evexpr(): !\n");
if (*sattr & TDB)
- error(seg_error);
+ 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 '~':
//printf("evexpr(): ~\n");
if (*sattr & TDB)
- error(seg_error);
+ 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:
sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
- error(seg_error);
+ 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;
- *sval = *sval <= sval[1];
break;
+
case GE:
//printf("evexpr(): GE\n");
sattr--;
sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
- error(seg_error);
+ 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;
- *sval = *sval >= sval[1];
break;
+
case '>':
//printf("evexpr(): >\n");
sattr--;
sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
- error(seg_error);
+ 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;
- *sval = *sval > sval[1];
break;
+
case '<':
//printf("evexpr(): <\n");
sattr--;
sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
- error(seg_error);
+ return error(seg_error);
- *sattr = ABS | DEFINED;
- *sval = *sval < sval[1];
+ // 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:
//printf("evexpr(): NE\n");
sattr--;
sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
- error(seg_error);
+ return error(seg_error);
- *sattr = ABS | DEFINED;
- *sval = *sval != sval[1];
+ // 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 '=':
//printf("evexpr(): =\n");
sattr--;
sval--;
if ((*sattr & TDB) != (sattr[1] & TDB))
- error(seg_error);
+ 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
- *sattr = ABS | DEFINED;
- *sval = *sval == sval[1];
break;
- // All other binary operators must have two ABS items
- // to work with. They all produce an ABS value.
+
+ // 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:
//printf("evexpr(): default\n");
- // GH - Removed for v1.0.15 as part of the fix for indexed loads.
- //if ((*sattr & (TEXT|DATA|BSS)) || (*--sattr & (TEXT|DATA|BSS)))
- //error(seg_error);
- *sattr = ABS | DEFINED; // Expr becomes absolute
- switch ((int)tk[-1])
+ switch ((int)tk.u32[-1])
{
case '*':
sval--;
- sattr--; // Pop attrib
+ sattr--;
//printf("--> NxN: %i x %i = ", *sval, sval[1]);
- *sval *= sval[1];
+ // 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];
+ }
//printf("%i\n", *sval);
+
+//no *sattr = ABS | DEFINED | attr; // Expr becomes absolute
break;
+
case '/':
sval--;
- sattr--; // Pop attrib
+ sattr--;
+//printf("--> N/N: %i / %i = ", sval[0], sval[1]);
+ // Get FLOAT attribute, if any
+ attr = (sattr[0] | sattr[1]) & FLOAT;
- if (sval[1] == 0)
- return error("divide by zero");
+ 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");
//printf("--> N/N: %i / %i = ", sval[0], sval[1]);
- // Compiler is picky here: Without casting these, it discards
- // the sign if dividing a negative # by a positive one,
- // creating a bad result. :-/
- // Probably a side effect of using VALUE intead of ints.
- *sval = (int)sval[0] / (int)sval[1];
+
+ // 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];
+ }
//printf("%i\n", *sval);
+
+//no *sattr = ABS | DEFINED | attr; // Expr becomes absolute
break;
+
case '%':
sval--;
- sattr--; // Pop attrib
+ 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];
+//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
+
case SHL:
sval--;
sattr--; // Pop attrib
+
+ if ((*sattr | sattr[1]) & FLOAT)
+ return error("floating point numbers not allowed with operator '<<'.");
+
*sval <<= sval[1];
+//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
+
case SHR:
sval--;
sattr--; // Pop attrib
+
+ if ((*sattr | sattr[1]) & FLOAT)
+ return error("floating point numbers not allowed with operator '>>'.");
+
*sval >>= sval[1];
+//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
+
case '&':
sval--;
sattr--; // Pop attrib
+
+ if ((*sattr | sattr[1]) & FLOAT)
+ return error("floating point numbers not allowed with operator '&'.");
+
*sval &= sval[1];
+//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
+
case '^':
sval--;
sattr--; // Pop attrib
+
+ if ((*sattr | sattr[1]) & FLOAT)
+ return error("floating point numbers not allowed with operator '^'.");
+
*sval ^= sval[1];
+//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
+
case '|':
sval--;
- sattr--; // Pop attrib
+ sattr--;
+
+ if ((*sattr | sattr[1]) & FLOAT)
+ return error("floating point numbers not allowed with operator '|'.");
+
*sval |= sval[1];
+//no *sattr = ABS | DEFINED; // Expr becomes absolute
break;
+
default:
- interror(5); // Bad operator in expression stream
+ // Bad operator in expression stream (this should never happen!)
+ interror(5);
}
}
}
if (a_esym != NULL)
*a_esym = esym;
- // sym_seg added in 1.0.16 to solve a problem with forward symbols in
- // expressions where absolute values also existed. The absolutes were
- // overiding the symbol segments and not being included :(
- //*a_attr = *sattr | sym_seg; // Copy value + attrib
-
- *a_attr = *sattr; // Copy value + attrib
+ // 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;
+}
+