]> Shamusworld >> Repos - rmac/blobdiff - expr.c
Long forgotten helper script which is used to create tables for VS builds
[rmac] / expr.c
diff --git a/expr.c b/expr.c
index 8e2c33aa11f93e37538e13a75a91daa293d5832f..ebb17b86d5b7033d04b98b7b5568adfd9379f5c5 100644 (file)
--- a/expr.c
+++ b/expr.c
@@ -1,7 +1,7 @@
 //
-// 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, 2011 Reboot and Friends
+// Copyright (C) 199x Landon Dyer, 2011-2019 Reboot and Friends
 // RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986
 // Source utilised with the kind permission of Landon Dyer
 //
 #include "symbol.h"
 #include "token.h"
 
-#define DEF_KW                                         // Declare keyword values 
+#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 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 
+       ')', ']', '}', 0,                               // CPAR
        CR_DEFINED, CR_REFERENCED,              // SUNARY (special unary)
        CR_STREQ, CR_MACDEF,
-       CR_DATE, CR_TIME, 0,
-       '!', '~', UNMINUS, 0,                   // UNARY
-       '*', '/', '%', 0,                               // MULT 
-       '+', '-', 0,                                    // ADD 
-       SHL, SHR, 0,                                    // SHIFT 
-       LE, GE, '<', '>', NE, '=', 0,   // REL 
-       '&', 0,                                                 // AND 
-       '^', 0,                                                 // XOR 
-       '|', 0,                                                 // OR 
-       1                                                               // (the end) 
+       CR_DATE, CR_TIME,
+       CR_ABSCOUNT, 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 TOKEN * evalTokenBuffer;                // Deposit tokens here (this is really a
-                                                                       // pointer to exprbuf from direct.c)
-                                                                       // (Can also be from others, like
-                                                                       // riscasm.c)
-static 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);
@@ -76,18 +78,17 @@ static VALUE str_value(char * p)
 //
 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++;
-               else 
+               else
                        tokenClass[(int)(*p)] = (char)i;
        }
 
@@ -100,19 +101,17 @@ void InitExpression(void)
 //
 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;
@@ -121,50 +120,68 @@ int expr0(void)
 
 //
 // 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)
 {
-       int class;
-       TOKEN t;
-       SYM * sy;
-       char * p, * p2;
+       char * p;
        WORD w;
-       int j;
 
-       class = tokenClass[*tok];
+       int class = tokenClass[*tok];
 
-       if (*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:
+                       if (cursect != ABS)
+                       {
+                               *evalTokenBuffer.u32++ = CONST;
+                               *evalTokenBuffer.u64++ = sect[ABS].sloc;
+                       }
+                       else
+                       {
+                               *evalTokenBuffer.u32++ = CONST;
+                               *evalTokenBuffer.u64++ = sloc;
+                       }
+                       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;
@@ -176,10 +193,11 @@ getsym:
                                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)
@@ -194,16 +212,16 @@ getsym:
                        if (*tok != SYMBOL && *tok != STRING)
                                return error(str_error);
 
-                       p = 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;
                }
        }
-       else 
+       else
                return expr2();
 
        return OK;
@@ -215,20 +233,27 @@ getsym:
 //
 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);
@@ -236,28 +261,29 @@ int expr2(void)
                // Check register bank usage
                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);
+                       if ((regbank == BANK_0) && (sy->sattre & BANK_1) && !altbankok)
+                               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 '[':
@@ -265,21 +291,35 @@ int expr2(void)
                        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");
@@ -292,7 +332,7 @@ int expr2(void)
 //
 // 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
@@ -301,9 +341,11 @@ int expr(TOKEN * otk, VALUE * a_value, WORD * a_attr, SYM ** a_esym)
        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.
@@ -311,15 +353,32 @@ int expr(TOKEN * otk, VALUE * a_value, WORD * a_attr, SYM ** a_esym)
        //         followed by the value 101, it will trigger a bad evaluation here.
        //         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)
-               || (((*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)
@@ -329,24 +388,42 @@ int expr(TOKEN * otk, VALUE * a_value, WORD * a_attr, SYM ** a_esym)
                }
                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;
@@ -378,7 +455,7 @@ if (symbol)
                        // 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;
                        }
@@ -386,14 +463,14 @@ if (symbol)
                        // Check register bank usage
                        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);
+                               if ((regbank == BANK_0) && (symbol->sattre & BANK_1) && !altbankok)
+                                       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
@@ -402,7 +479,7 @@ 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++ = symbolNum;
+                       *evalTokenBuffer.u32++ = symbolNum;
                        symbolPtr[symbolNum] = symbol;
                        symbolNum++;
 #endif
@@ -416,7 +493,7 @@ be converted from a linked list into an array).
 All that extra crap that was put into the svalue when doing the equr stuff is
 thrown away right here. What the hell is it for?
 */
-                       if (symbol->sattre & EQUATEDREG) 
+                       if (symbol->sattre & EQUATEDREG)
                                *a_value &= 0x1F;
 
                        *a_attr = (WORD)(symbol->sattr & ~GLOBAL);
@@ -427,23 +504,28 @@ thrown away right here. What the hell is it for?
 
                        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);
 }
 
@@ -452,30 +534,27 @@ thrown away right here. What the hell is it for?
 // 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->sattr |= REFERENCED;                // Set "referenced" bit 
+                       sy = symbolPtr[*tk.u32++];
+                       sy->sattr |= REFERENCED;                // Set "referenced" bit
 
                        if (!(sy->sattr & DEFINED))
                        {
@@ -494,57 +573,106 @@ int evexpr(TOKEN * tk, VALUE * a_value, WORD * a_attr, SYM ** a_esym)
                        }
 
                        if (sy->sattr & DEFINED)
-                       {
                                *++sval = sy->svalue;           // Push symbol's value
-                       }
                        else
-                       {
-                               *++sval = 0;                            // 0 for undefined symbols 
-                       }
+                               *++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:
-                       // 
+                       //
                        //                ABS    Sect     Other
                        //     ----------------------------
                        //   ABS     |  ABS   |  Sect  |  Other |
                        //   Sect    |  Sect  |  [1]   |  Error |
                        //   Other   |  Other |  Error |  [1]   |
                        //      ----------------------------
-                       // 
+                       //
                        //   [1] + : Error
                        //       - : ABS
+
                case '+':
 //printf("evexpr(): +\n");
                        --sval;                                                 // Pop value
-                       --sattr;                                                // Pop attrib 
-                       *sval += sval[1];                               // Compute value
+                       --sattr;                                                // Pop attrib
+//printf("--> N+N: %i + %i = ", *sval, sval[1]);
+                       // 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 
-                       *sval -= sval[1];                               // Compute 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]);
@@ -557,160 +685,408 @@ 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);
+
+                       if (*sattr & FLOAT)
+                               return error(noflt_error);
 
-                       *sval = -(int)*sval;
+                       *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:
 //printf("evexpr(): LE\n");
-                       --sattr;
-                       --sval;
+                       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 GE:
 //printf("evexpr(): GE\n");
-                       --sattr;
-                       --sval;
+                       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;
+                       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;
+                       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;
+                       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;
+                       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 
-                               *sval *= sval[1];
+                               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;                                        // Pop attrib 
+                               sval--;
+                               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. :-/
+                                       // Definitely a side effect of using uint32_ts intead of
+                                       // ints.
+                                       *sval = (int32_t)sval[0] / (int32_t)sval[1];
+                               }
+//printf("%i\n", *sval);
 
-                               *sval /= sval[1];
+//no                           *sattr = ABS | DEFINED | attr;          // Expr becomes absolute
                                break;
+
                        case '%':
-                               --sval;
-                               --sattr;                                        // Pop attrib 
+                               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 
+                               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 
+                               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 
+                               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 
+                               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 
+                               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:
-                               interror(5);                            // Bad operator in expression stream
+                               // Bad operator in expression stream (this should never happen!)
+                               interror(5);
                        }
                }
        }
@@ -721,14 +1097,34 @@ printf("EVEXPR (-): sym1 = %X, sym2 = %X\n", attr, sattr[1]);
        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;
+}
+