-int evexpr(TOKEN *tk, VALUE *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)
- switch((int)*tk++) {
- case SYMBOL:
- sy = (SYM *)*tk++;
- sy->sattr |= REFERENCED; // Set "referenced" bit
-
- if(!(sy->sattr & DEFINED)) {
- if(!(sy->sattr & GLOBAL)) { // Reference to undefined symbol
- *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 & (TEXT|DATA|BSS));
- break;
- case CONST:
- *++sval = *tk++; // Push value
- *++sattr = ABS|DEFINED; // Push simple attribs
- break;
- case ACONST:
- *++sval = *tk++; // Push value
- *++sattr = (WORD)*tk++; // 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
- *sval += sval[1]; // Compute value
-
- if(!(*sattr & (TEXT|DATA|BSS)))
- *sattr = sattr[1];
- else if(sattr[1] & (TEXT|DATA|BSS))
- return(error(seg_error));
- break;
- case '-':
- --sval; // Pop value
- --sattr; // Pop attrib
- *sval -= sval[1]; // Compute value
-
- attr = (WORD)(*sattr & (TEXT|DATA|BSS));
- if(!attr)
- *sattr = sattr[1];
- else if(sattr[1] & (TEXT|DATA|BSS)) {
- if(!(attr & sattr[1])) {
- return(error(seg_error));
- } else {
- *sattr &= ~(TEXT|DATA|BSS);
- }
- }
- break;
- // Unary operators only work on ABS items
- case UNMINUS:
- if(*sattr & (TEXT|DATA|BSS))
- error(seg_error);
- *sval = -(int)*sval;
- *sattr = ABS|DEFINED; // Expr becomes absolute
- break;
- case '!':
- if(*sattr & (TEXT|DATA|BSS))
- error(seg_error);
- *sval = !*sval;
- *sattr = ABS|DEFINED; // Expr becomes absolute
- break;
- case '~':
- if(*sattr & (TEXT|DATA|BSS))
- error(seg_error);
- *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)) error(seg_error);
- *sattr = ABS|DEFINED;
- *sval = *sval <= sval[1];
- break;
- case GE:
- --sattr;
- --sval;
- if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error);
- *sattr = ABS|DEFINED;
- *sval = *sval >= sval[1];
- break;
- case '>':
- --sattr;
- --sval;
- if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error);
- *sattr = ABS|DEFINED;
- *sval = *sval > sval[1];
- break;
- case '<':
- --sattr;
- --sval;
- if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error);
- *sattr = ABS|DEFINED;
- *sval = *sval < sval[1];
- break;
- case NE:
- --sattr;
- --sval;
- if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error);
- *sattr = ABS|DEFINED;
- *sval = *sval != sval[1];
- break;
- case '=':
- --sattr;
- --sval;
- if((*sattr & TDB) != (sattr[1] & TDB)) error(seg_error);
- *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.
- default:
- // 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]) {
- case '*':
- --sval;
- --sattr; // Pop attrib
- *sval *= sval[1];
- break;
- case '/':
- --sval;
- --sattr; // Pop attrib
- if(sval[1] == 0)
- return(error("divide by zero"));
- *sval /= sval[1];
- break;
- case '%':
- --sval;
- --sattr; // Pop attrib
- if(sval[1] == 0)
- return(error("mod (%) by zero"));
- *sval %= sval[1];
- break;
- case SHL:
- --sval;
- --sattr; // Pop attrib
- *sval <<= sval[1];
- break;
- case SHR:
- --sval;
- --sattr; // Pop attrib
- *sval >>= sval[1];
- break;
- case '&':
- --sval;
- --sattr; // Pop attrib
- *sval &= sval[1];
- break;
- case '^':
- --sval;
- --sattr; // Pop attrib
- *sval ^= sval[1];
- break;
- case '|':
- --sval;
- --sattr; // Pop attrib
- *sval |= sval[1];
- break;
- default:
- interror(5); // Bad operator in expression stream
- }
- }
-
- if(esym != NULL) *sattr &= ~DEFINED;
- 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
- *a_value = *sval;
-
- return(OK);
+ case '-':
+//printf("evexpr(): -\n");
+ --sval; // Pop value
+ --sattr; // Pop attrib
+//printf("--> N-N: %i - %i = ", *sval, sval[1]);
+ // 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]);
+#endif
+ // 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:
+//printf("evexpr(): UNMINUS\n");
+ 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)
+//printf("evexpr(): UNLT\n");
+ 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)
+//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)
+ 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)
+ 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:
+//printf("evexpr(): LE\n");
+ 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:
+//printf("evexpr(): GE\n");
+ 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 '>':
+//printf("evexpr(): >\n");
+ 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 '<':
+//printf("evexpr(): <\n");
+ 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:
+//printf("evexpr(): NE\n");
+ 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 '=':
+//printf("evexpr(): =\n");
+ 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:
+//printf("evexpr(): default\n");
+
+ switch ((int)tk.u32[-1])
+ {
+ case '*':
+ sval--;
+ sattr--;
+//printf("--> NxN: %i x %i = ", *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--;
+//printf("--> N/N: %i / %i = ", sval[0], sval[1]);
+ // 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");
+//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. :-/
+ // 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--;
+
+ 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--;
+
+ 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:
+ // 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;