]> git.sur5r.net Git - cc65/blobdiff - src/ca65/studyexpr.c
Normalized code.
[cc65] / src / ca65 / studyexpr.c
index e125dec99eefab42c563f253ca565ceea46b40b7..cb2795bd3b5224012a3b5cc8188be6b3b34d4cf7 100644 (file)
@@ -6,10 +6,10 @@
 /*                                                                           */
 /*                                                                           */
 /*                                                                           */
-/* (C) 2003      Ullrich von Bassewitz                                       */
-/*               Römerstraße 52                                              */
-/*               D-70794 Filderstadt                                         */
-/* EMail:        uz@cc65.org                                                 */
+/* (C) 2003-2012, Ullrich von Bassewitz                                      */
+/*                Roemerstrasse 52                                           */
+/*                D-70794 Filderstadt                                        */
+/* EMail:         uz@cc65.org                                                */
 /*                                                                           */
 /*                                                                           */
 /* This software is provided 'as-is', without any expressed or implied       */
 
 
 
+#include <string.h>
+
 /* common */
 #include "check.h"
-#include "print.h"
+#include "debugflag.h"
 #include "shift.h"
+#include "xmalloc.h"
+
+/* ca65 */
+#include "error.h"
+#include "segment.h"
+#include "studyexpr.h"
+#include "symtab.h"
+#include "ulabel.h"
+
+
+
+/*****************************************************************************/
+/*                              struct ExprDesc                              */
+/*****************************************************************************/
+
+
+
+ExprDesc* ED_Init (ExprDesc* ED)
+/* Initialize an ExprDesc structure for use with StudyExpr */
+{
+    ED->Flags     = ED_OK;
+    ED->AddrSize  = ADDR_SIZE_DEFAULT;
+    ED->Val       = 0;
+    ED->SymCount  = 0;
+    ED->SymLimit  = 0;
+    ED->SymRef    = 0;
+    ED->SecCount  = 0;
+    ED->SecLimit  = 0;
+    ED->SecRef    = 0;
+    return ED;
+}
+
+
+
+void ED_Done (ExprDesc* ED)
+/* Delete allocated memory for an ExprDesc. */
+{
+    xfree (ED->SymRef);
+    xfree (ED->SecRef);
+}
+
+
+
+int ED_IsConst (const ExprDesc* D)
+/* Return true if the expression is constant */
+{
+    unsigned I;
+
+    if (D->Flags & ED_TOO_COMPLEX) {
+        return 0;
+    }
+    for (I = 0; I < D->SymCount; ++I) {
+        if (D->SymRef[I].Count != 0) {
+            return 0;
+        }
+    }
+    for (I = 0; I < D->SecCount; ++I) {
+        if (D->SecRef[I].Count != 0) {
+            return 0;
+        }
+    }
+    return 1;
+}
+
+
+
+static int ED_IsValid (const ExprDesc* D)
+/* Return true if the expression is valid, that is, neither the ERROR nor the
+ * TOO_COMPLEX flags are set.
+ */
+{
+    return ((D->Flags & (ED_ERROR | ED_TOO_COMPLEX)) == 0);
+}
+
+
+
+static int ED_HasError (const ExprDesc* D)
+/* Return true if the expression has an error. */
+{
+    return ((D->Flags & ED_ERROR) != 0);
+}
+
+
+
+static void ED_Invalidate (ExprDesc* D)
+/* Set the TOO_COMPLEX flag for D */
+{
+    D->Flags |= ED_TOO_COMPLEX;
+}
+
+
+
+static void ED_SetError (ExprDesc* D)
+/* Set the TOO_COMPLEX and ERROR flags for D */
+{
+    D->Flags |= (ED_ERROR | ED_TOO_COMPLEX);
+}
+
+
+
+static void ED_UpdateAddrSize (ExprDesc* ED, unsigned char AddrSize)
+/* Update the address size of the expression */
+{
+    if (ED_IsValid (ED)) {
+        /* ADDR_SIZE_DEFAULT may get overridden */
+        if (ED->AddrSize == ADDR_SIZE_DEFAULT || AddrSize > ED->AddrSize) {
+            ED->AddrSize = AddrSize;
+        }
+    } else {
+        /* ADDR_SIZE_DEFAULT takes precedence */
+        if (ED->AddrSize != ADDR_SIZE_DEFAULT) {
+            if (AddrSize == ADDR_SIZE_DEFAULT || AddrSize > ED->AddrSize) {
+                ED->AddrSize = AddrSize;
+            }
+        }
+    }
+}
+
+
+
+static void ED_MergeAddrSize (ExprDesc* ED, const ExprDesc* Right)
+/* Merge the address sizes of two expressions into ED */
+{
+    if (ED->AddrSize == ADDR_SIZE_DEFAULT) {
+        /* If ED is valid, ADDR_SIZE_DEFAULT gets always overridden, otherwise
+         * it takes precedence over anything else.
+         */
+        if (ED_IsValid (ED)) {
+            ED->AddrSize = Right->AddrSize;
+        }
+    } else if (Right->AddrSize == ADDR_SIZE_DEFAULT) {
+        /* If Right is valid, ADDR_SIZE_DEFAULT gets always overridden,
+         * otherwise it takes precedence over anything else.
+         */
+        if (!ED_IsValid (Right)) {
+            ED->AddrSize = Right->AddrSize;
+        }
+    } else {
+        /* Neither ED nor Right has a default address size, use the larger of
+         * the two.
+         */
+        if (Right->AddrSize > ED->AddrSize) {
+            ED->AddrSize = Right->AddrSize;
+        }
+    }
+}
+
+
+
+static ED_SymRef* ED_FindSymRef (ExprDesc* ED, SymEntry* Sym)
+/* Find a symbol reference and return it. Return NULL if the reference does
+ * not exist.
+ */
+{
+    unsigned I;
+    ED_SymRef* SymRef;
+    for (I = 0, SymRef = ED->SymRef; I < ED->SymCount; ++I, ++SymRef) {
+        if (SymRef->Ref == Sym) {
+            return SymRef;
+        }
+    }
+    return 0;
+}
+
+
+
+static ED_SecRef* ED_FindSecRef (ExprDesc* ED, unsigned Sec)
+/* Find a section reference and return it. Return NULL if the reference does
+ * not exist.
+ */
+{
+    unsigned I;
+    ED_SecRef* SecRef;
+    for (I = 0, SecRef = ED->SecRef; I < ED->SecCount; ++I, ++SecRef) {
+        if (SecRef->Ref == Sec) {
+            return SecRef;
+        }
+    }
+    return 0;
+}
+
+
+
+static ED_SymRef* ED_AllocSymRef (ExprDesc* ED, SymEntry* Sym)
+/* Allocate a new symbol reference and return it. The count of the new
+ * reference will be set to zero, and the reference itself to Sym.
+ */
+{
+    ED_SymRef* SymRef;
+
+    /* Make sure we have enough SymRef slots */
+    if (ED->SymCount >= ED->SymLimit) {
+        ED->SymLimit *= 2;
+        if (ED->SymLimit == 0) {
+            ED->SymLimit = 2;
+        }
+        ED->SymRef = xrealloc (ED->SymRef, ED->SymLimit * sizeof (ED->SymRef[0]));
+    }
+
+    /* Allocate a new slot */
+    SymRef = ED->SymRef + ED->SymCount++;
+
+    /* Initialize the new struct and return it */
+    SymRef->Count = 0;
+    SymRef->Ref   = Sym;
+    return SymRef;
+}
+
+
+
+static ED_SecRef* ED_AllocSecRef (ExprDesc* ED, unsigned Sec)
+/* Allocate a new section reference and return it. The count of the new
+ * reference will be set to zero, and the reference itself to Sec.
+ */
+{
+    ED_SecRef* SecRef;
+
+    /* Make sure we have enough SecRef slots */
+    if (ED->SecCount >= ED->SecLimit) {
+        ED->SecLimit *= 2;
+        if (ED->SecLimit == 0) {
+            ED->SecLimit = 2;
+        }
+        ED->SecRef = xrealloc (ED->SecRef, ED->SecLimit * sizeof (ED->SecRef[0]));
+    }
+
+    /* Allocate a new slot */
+    SecRef = ED->SecRef + ED->SecCount++;
+
+    /* Initialize the new struct and return it */
+    SecRef->Count = 0;
+    SecRef->Ref   = Sec;
+    return SecRef;
+}
+
+
+
+static ED_SymRef* ED_GetSymRef (ExprDesc* ED, SymEntry* Sym)
+/* Get a symbol reference and return it. If the symbol reference does not
+ * exist, a new one is created and returned.
+ */
+{
+    ED_SymRef* SymRef = ED_FindSymRef (ED, Sym);
+    if (SymRef == 0) {
+        SymRef = ED_AllocSymRef (ED, Sym);
+    }
+    return SymRef;
+}
+
+
+
+static ED_SecRef* ED_GetSecRef (ExprDesc* ED, unsigned Sec)
+/* Get a section reference and return it. If the section reference does not
+ * exist, a new one is created and returned.
+ */
+{
+    ED_SecRef* SecRef = ED_FindSecRef (ED, Sec);
+    if (SecRef == 0) {
+        SecRef = ED_AllocSecRef (ED, Sec);
+    }
+    return SecRef;
+}
+
+
+
+static void ED_MergeSymRefs (ExprDesc* ED, const ExprDesc* New)
+/* Merge the symbol references from New into ED */
+{
+    unsigned I;
+    for (I = 0; I < New->SymCount; ++I) {
+
+        /* Get a pointer to the SymRef entry */
+        const ED_SymRef* NewRef = New->SymRef + I;
+
+        /* Get the corresponding entry in ED */
+        ED_SymRef* SymRef = ED_GetSymRef (ED, NewRef->Ref);
+
+        /* Sum up the references */
+        SymRef->Count += NewRef->Count;
+    }
+}
+
+
+
+static void ED_MergeSecRefs (ExprDesc* ED, const ExprDesc* New)
+/* Merge the section references from New into ED */
+{
+    unsigned I;
+    for (I = 0; I < New->SecCount; ++I) {
+
+        /* Get a pointer to the SymRef entry */
+        const ED_SecRef* NewRef = New->SecRef + I;
+
+        /* Get the corresponding entry in ED */
+        ED_SecRef* SecRef = ED_GetSecRef (ED, NewRef->Ref);
+
+        /* Sum up the references */
+        SecRef->Count += NewRef->Count;
+    }
+}
+
+
+
+static void ED_MergeRefs (ExprDesc* ED, const ExprDesc* New)
+/* Merge all references from New into ED */
+{
+    ED_MergeSymRefs (ED, New);
+    ED_MergeSecRefs (ED, New);
+}
+
+
+
+static void ED_NegRefs (ExprDesc* D)
+/* Negate the references in ED */
+{
+    unsigned I;
+    for (I = 0; I < D->SymCount; ++I) {
+        D->SymRef[I].Count = -D->SymRef[I].Count;
+    }
+    for (I = 0; I < D->SecCount; ++I) {
+        D->SecRef[I].Count = -D->SecRef[I].Count;
+    }
+}
+
+
+
+static void ED_Add (ExprDesc* ED, const ExprDesc* Right)
+/* Calculate ED = ED + Right, update address size in ED */
+{
+    ED->Val += Right->Val;
+    ED_MergeRefs (ED, Right);
+    ED_MergeAddrSize (ED, Right);
+}
+
+
+
+static void ED_Sub (ExprDesc* ED, const ExprDesc* Right)
+/* Calculate ED = ED - Right, update address size in ED */
+{
+    ExprDesc D = *Right;        /* Temporary */
+    ED_NegRefs (&D);
+
+    ED->Val -= Right->Val;
+    ED_MergeRefs (ED, &D);      /* Merge negatives */
+    ED_MergeAddrSize (ED, Right);
+}
+
+
+
+static void ED_Mul (ExprDesc* ED, const ExprDesc* Right)
+/* Calculate ED = ED * Right, update address size in ED */
+{
+    unsigned I;
+
+    ED->Val *= Right->Val;
+    for (I = 0; I < ED->SymCount; ++I) {
+        ED->SymRef[I].Count *= Right->Val;
+    }
+    for (I = 0; I < ED->SecCount; ++I) {
+        ED->SecRef[I].Count *= Right->Val;
+    }
+    ED_MergeAddrSize (ED, Right);
+}
+
+
+
+static void ED_Neg (ExprDesc* D)
+/* Negate an expression */
+{
+    D->Val = -D->Val;
+    ED_NegRefs (D);
+}
+
+
+
+static void ED_Move (ExprDesc* From, ExprDesc* To)
+/* Move the data from one ExprDesc to another. Old data is freed, and From
+ * is prepared to that ED_Done may be called safely.
+ */
+{
+    /* Delete old data */
+    ED_Done (To);
+
+    /* Move the data */
+    *To = *From;
+
+    /* Cleanup From */
+    ED_Init (From);
+}
+
+
+
+/*****************************************************************************/
+/*                                   Code                                    */
+/*****************************************************************************/
+
+
+
+static void StudyExprInternal (ExprNode* Expr, ExprDesc* D);
+/* Study an expression tree and place the contents into D */
+
+
+
+static unsigned char GetConstAddrSize (long Val)
+/* Get the address size of a constant */
+{
+    if ((Val & ~0xFFL) == 0) {
+        return ADDR_SIZE_ZP;
+    } else if ((Val & ~0xFFFFL) == 0) {
+        return ADDR_SIZE_ABS;
+    } else if ((Val & ~0xFFFFFFL) == 0) {
+        return ADDR_SIZE_FAR;
+    } else {
+        return ADDR_SIZE_LONG;
+    }
+}
+
+
+
+static void StudyBinaryExpr (ExprNode* Expr, ExprDesc* D)
+/* Study a binary expression subtree. This is a helper function for StudyExpr
+ * used for operations that succeed when both operands are known and constant.
+ * It evaluates the two subtrees and checks if they are constant. If they
+ * aren't constant, it will set the TOO_COMPLEX flag, and merge references.
+ * Otherwise the first value is returned in D->Val, the second one in D->Right,
+ * so the actual operation can be done by the caller.
+ */
+{
+    ExprDesc Right;
+
+    /* Study the left side of the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* Study the right side of the expression */
+    ED_Init (&Right);
+    StudyExprInternal (Expr->Right, &Right);
+
+    /* Check if we can handle the operation */
+    if (ED_IsConst (D) && ED_IsConst (&Right)) {
+
+        /* Remember the constant value from Right */
+        D->Right = Right.Val;
+
+    } else {
+
+        /* Cannot evaluate */
+        ED_Invalidate (D);
+
+        /* Merge references and update address size */
+        ED_MergeRefs (D, &Right);
+        ED_MergeAddrSize (D, &Right);
+
+    }
+
+    /* Cleanup Right */
+    ED_Done (&Right);
+}
+
+
+
+static void StudyLiteral (ExprNode* Expr, ExprDesc* D)
+/* Study a literal expression node */
+{
+    /* This one is easy */
+    D->Val      = Expr->V.IVal;
+    D->AddrSize = GetConstAddrSize (D->Val);
+}
+
+
+
+static void StudySymbol (ExprNode* Expr, ExprDesc* D)
+/* Study a symbol expression node */
+{
+    /* Get the symbol from the expression */
+    SymEntry* Sym = Expr->V.Sym;
+
+    /* If the symbol is defined somewhere, it has an expression associated.
+     * In this case, just study the expression associated with the symbol,
+     * but mark the symbol so if we encounter it twice, we know that we have
+     * a circular reference.
+     */
+    if (SymHasExpr (Sym)) {
+
+        if (SymHasUserMark (Sym)) {
+            LIError (&Sym->DefLines,
+                     "Circular reference in definition of symbol `%m%p'",
+                     GetSymName (Sym));
+            ED_SetError (D);
+        } else {
+
+            unsigned char AddrSize;
+
+            /* Mark the symbol and study its associated expression */
+            SymMarkUser (Sym);
+            StudyExprInternal (GetSymExpr (Sym), D);
+            SymUnmarkUser (Sym);
+
+            /* If requested and if the expression is valid, dump it */
+            if (Debug > 0 && !ED_HasError (D)) {
+                DumpExpr (Expr, SymResolve);
+            }
+
+            /* If the symbol has an explicit address size, use it. This may
+             * lead to range errors later (maybe even in the linker stage), if
+             * the user lied about the address size, but for now we trust him.
+             */
+            AddrSize = GetSymAddrSize (Sym);
+            if (AddrSize != ADDR_SIZE_DEFAULT) {
+                D->AddrSize = AddrSize;
+            }
+        }
+
+    } else if (SymIsImport (Sym)) {
+
+        /* The symbol is an import. Track the symbols used and update the
+         * address size.
+         */
+        ED_SymRef* SymRef = ED_GetSymRef (D, Sym);
+        ++SymRef->Count;
+        ED_UpdateAddrSize (D, GetSymAddrSize (Sym));
+
+    } else {
+
+        unsigned char AddrSize;
+        SymTable* Parent;
+
+        /* The symbol is undefined. Track symbol usage but set the "too
+         * complex" flag, since we cannot evaluate the final result.
+         */
+        ED_SymRef* SymRef = ED_GetSymRef (D, Sym);
+        ++SymRef->Count;
+        ED_Invalidate (D);
+
+        /* Since the symbol may be a forward, and we may need a statement
+         * about the address size, check higher lexical levels for a symbol
+         * with the same name and use its address size if we find such a
+         * symbol which is defined.
+         */
+        AddrSize = GetSymAddrSize (Sym);
+        Parent = GetSymParentScope (Sym);
+        if (AddrSize == ADDR_SIZE_DEFAULT && Parent != 0) {
+            SymEntry* H = SymFindAny (Parent, GetSymName (Sym));
+            if (H) {
+                AddrSize = GetSymAddrSize (H);
+                if (AddrSize != ADDR_SIZE_DEFAULT) {
+                    D->AddrSize = AddrSize;
+                }
+            }
+        } else {
+            D->AddrSize = AddrSize;
+        }
+    }
+}
+
+
+
+static void StudySection (ExprNode* Expr, ExprDesc* D)
+/* Study a section expression node */
+{
+    /* Get the section reference */
+    ED_SecRef* SecRef = ED_GetSecRef (D, Expr->V.SecNum);
+
+    /* Update the data and the address size */
+    ++SecRef->Count;
+    ED_UpdateAddrSize (D, GetSegAddrSize (SecRef->Ref));
+}
+
+
+
+static void StudyULabel (ExprNode* Expr, ExprDesc* D)
+/* Study an unnamed label expression node */
+{
+    /* If we can resolve the label, study the expression associated with it,
+     * otherwise mark the expression as too complex to evaluate.
+     */
+    if (ULabCanResolve ()) {
+        /* We can resolve the label */
+        StudyExprInternal (ULabResolve (Expr->V.IVal), D);
+    } else {
+        ED_Invalidate (D);
+    }
+}
+
+
+
+static void StudyPlus (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_PLUS binary expression node */
+{
+    ExprDesc Right;
+
+    /* Study the left side of the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* Study the right side of the expression */
+    ED_Init (&Right);
+    StudyExprInternal (Expr->Right, &Right);
+
+    /* Check if we can handle the operation */
+    if (ED_IsValid (D) && ED_IsValid (&Right)) {
+
+        /* Add both */
+        ED_Add (D, &Right);
+
+    } else {
+
+        /* Cannot evaluate */
+        ED_Invalidate (D);
+
+        /* Merge references and update address size */
+        ED_MergeRefs (D, &Right);
+        ED_MergeAddrSize (D, &Right);
+
+    }
+
+    /* Done */
+    ED_Done (&Right);
+}
+
+
+
+static void StudyMinus (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_MINUS binary expression node */
+{
+    ExprDesc Right;
+
+    /* Study the left side of the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* Study the right side of the expression */
+    ED_Init (&Right);
+    StudyExprInternal (Expr->Right, &Right);
+
+    /* Check if we can handle the operation */
+    if (ED_IsValid (D) && ED_IsValid (&Right)) {
+
+        /* Subtract both */
+        ED_Sub (D, &Right);
+
+    } else {
+
+        /* Cannot evaluate */
+        ED_Invalidate (D);
+
+        /* Merge references and update address size */
+        ED_MergeRefs (D, &Right);
+        ED_MergeAddrSize (D, &Right);
+
+    }
+
+    /* Done */
+    ED_Done (&Right);
+}
+
+
+
+static void StudyMul (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_MUL binary expression node */
+{
+    ExprDesc Right;
+
+    /* Study the left side of the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* Study the right side of the expression */
+    ED_Init (&Right);
+    StudyExprInternal (Expr->Right, &Right);
+
+    /* We can handle the operation if at least one of both operands is const
+     * and the other one is valid.
+     */
+    if (ED_IsConst (D) && ED_IsValid (&Right)) {
+
+        /* Multiplicate both, result goes into Right */
+        ED_Mul (&Right, D);
+
+        /* Move result into D */
+        ED_Move (&Right, D);
+
+    } else if (ED_IsConst (&Right) && ED_IsValid (D)) {
+
+        /* Multiplicate both */
+        ED_Mul (D, &Right);
+
+    } else {
+
+        /* Cannot handle this operation */
+        ED_Invalidate (D);
+
+    }
+
+    /* If we could not handle the op, merge references and update address size */
+    if (!ED_IsValid (D)) {
+        ED_MergeRefs (D, &Right);
+        ED_MergeAddrSize (D, &Right);
+    }
+
+    /* Done */
+    ED_Done (&Right);
+}
+
+
+
+static void StudyDiv (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_DIV binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        if (D->Right == 0) {
+            Error ("Division by zero");
+            ED_SetError (D);
+        } else {
+            D->Val /= D->Right;
+        }
+    }
+}
+
+
+
+static void StudyMod (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_MOD binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        if (D->Right == 0) {
+            Error ("Modulo operation with zero");
+            ED_SetError (D);
+        } else {
+            D->Val %= D->Right;
+        }
+    }
+}
+
+
+
+static void StudyOr (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_OR binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val |= D->Right;
+    }
+}
+
+
+
+static void StudyXor (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_XOR binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val ^= D->Right;
+    }
+}
+
+
+
+static void StudyAnd (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_AND binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val &= D->Right;
+    }
+}
+
+
+
+static void StudyShl (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_SHL binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val = shl_l (D->Val, D->Right);
+    }
+}
+
+
+
+static void StudyShr (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_SHR binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val = shr_l (D->Val, D->Right);
+    }
+}
+
+
+
+static void StudyEQ (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_EQ binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val = (D->Val == D->Right);
+    }
+
+    /* In any case, the result is 0 or 1 */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
+
+
+
+static void StudyNE (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_NE binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val = (D->Val != D->Right);
+    }
+
+    /* In any case, the result is 0 or 1 */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
+
+
+
+static void StudyLT (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_LT binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val = (D->Val < D->Right);
+    }
+
+    /* In any case, the result is 0 or 1 */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
+
+
+
+static void StudyGT (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_GT binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val = (D->Val > D->Right);
+    }
+
+    /* In any case, the result is 0 or 1 */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
+
+
+
+static void StudyLE (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_LE binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val = (D->Val <= D->Right);
+    }
+
+    /* In any case, the result is 0 or 1 */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
+
+
+
+static void StudyGE (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_GE binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val = (D->Val >= D->Right);
+    }
+
+    /* In any case, the result is 0 or 1 */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
+
 
-/* ca65 */
-#include "error.h"
-#include "segment.h"
-#include "studyexpr.h"
-#include "symtab.h"
-#include "ulabel.h"
 
+static void StudyBoolAnd (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_BOOLAND binary expression node */
+{
+    StudyExprInternal (Expr->Left, D);
+    if (ED_IsConst (D)) {
+        if (D->Val != 0) {   /* Shortcut op */
+            ED_Done (D);
+            ED_Init (D);
+            StudyExprInternal (Expr->Right, D);
+            if (ED_IsConst (D)) {
+                D->Val = (D->Val != 0);
+            } else {
+                ED_Invalidate (D);
+            }
+        }
+    } else {
+        ED_Invalidate (D);
+    }
+
+    /* In any case, the result is 0 or 1 */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
 
 
-/*****************************************************************************/
-/*                                          Code                                    */
-/*****************************************************************************/
 
+static void StudyBoolOr (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_BOOLOR binary expression node */
+{
+    StudyExprInternal (Expr->Left, D);
+    if (ED_IsConst (D)) {
+        if (D->Val == 0) {   /* Shortcut op */
+            ED_Done (D);
+            ED_Init (D);
+            StudyExprInternal (Expr->Right, D);
+            if (ED_IsConst (D)) {
+                D->Val = (D->Val != 0);
+            } else {
+                ED_Invalidate (D);
+            }
+        } else {
+            D->Val = 1;
+        }
+    } else {
+        ED_Invalidate (D);
+    }
 
+    /* In any case, the result is 0 or 1 */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
 
-ExprDesc* InitExprDesc (ExprDesc* ED)
-/* Initialize an ExprDesc structure for use with StudyExpr */
+
+
+static void StudyBoolXor (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_BOOLXOR binary expression node */
 {
-    ED->Val       = 0;
-    ED->TooComplex = 0;
-    ED->SymCount   = 0;
-    ED->SecCount   = 0;
-    return ED;
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val = (D->Val != 0) ^ (D->Right != 0);
+    }
+
+    /* In any case, the result is 0 or 1 */
+    D->AddrSize = ADDR_SIZE_ZP;
 }
 
 
 
-int ExprDescIsConst (const ExprDesc* ED)
-/* Return true if the expression is constant */
+static void StudyMax (ExprNode* Expr, ExprDesc* D)
+/* Study an MAX binary expression node */
+{
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val = (D->Val > D->Right)? D->Val : D->Right;
+    }
+}
+
+
+
+static void StudyMin (ExprNode* Expr, ExprDesc* D)
+/* Study an MIN binary expression node */
 {
-    return (ED->TooComplex == 0 && ED->SymCount == 0 && ED->SecCount == 0);
+    /* Use helper function */
+    StudyBinaryExpr (Expr, D);
+
+    /* If the result is valid, apply the operation */
+    if (ED_IsValid (D)) {
+        D->Val = (D->Val < D->Right)? D->Val : D->Right;
+    }
 }
 
 
 
-static void StudyBinaryExpr (ExprNode* Expr, ExprDesc* D)
-/* Study a binary expression subtree. Helper function for StudyExpr. */
-{
-    StudyExpr (Expr->Left, D, 1);
-    if (ExprDescIsConst (D)) {
-        D->Left = D->Val;
-        D->Val = 0;
-        StudyExpr (Expr->Right, D, 1);
-        if (!ExprDescIsConst (D)) {
-            D->TooComplex = 1;
-        }
+static void StudyUnaryMinus (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_UNARY_MINUS expression node */
+{
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* If it is valid, negate it */
+    if (ED_IsValid (D)) {
+        ED_Neg (D);
+    }
+}
+
+
+
+static void StudyNot (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_NOT expression node */
+{
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* We can handle only const expressions */
+    if (ED_IsConst (D)) {
+        D->Val = ~D->Val;
     } else {
-        D->TooComplex = 1;
+        ED_Invalidate (D);
     }
 }
 
 
 
-void StudyExpr (ExprNode* Expr, ExprDesc* D, int Sign)
-/* Study an expression tree and place the contents into D */
+static void StudySwap (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_SWAP expression node */
 {
-    SymEntry* Sym;
-    unsigned  Sec;
-    ExprDesc  SD;
-    ExprDesc  SD1;
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* We can handle only const expressions */
+    if (ED_IsConst (D)) {
+        D->Val = (D->Val & ~0xFFFFUL) | ((D->Val >> 8) & 0xFF) | ((D->Val << 8) & 0xFF00);
+    } else {
+        ED_Invalidate (D);
+    }
+}
+
+
+
+static void StudyBoolNot (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_BOOLNOT expression node */
+{
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* We can handle only const expressions */
+    if (ED_IsConst (D)) {
+        D->Val = (D->Val == 0);
+    } else {
+        ED_Invalidate (D);
+    }
+
+    /* In any case, the result is 0 or 1 */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
+
+
+
+static void StudyBank (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_BANK expression node */
+{
+    /* Study the expression extracting section references */
+    StudyExprInternal (Expr->Left, D);
+
+    /* The expression is always linker evaluated, so invalidate it */
+    ED_Invalidate (D);
+}
+
+
+
+static void StudyByte0 (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_BYTE0 expression node */
+{
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* We can handle only const expressions */
+    if (ED_IsConst (D)) {
+        D->Val = (D->Val & 0xFF);
+    } else {
+        ED_Invalidate (D);
+    }
+
+    /* In any case, the result is a zero page expression */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
+
+
+
+static void StudyByte1 (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_BYTE1 expression node */
+{
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* We can handle only const expressions */
+    if (ED_IsConst (D)) {
+        D->Val = (D->Val >> 8) & 0xFF;
+    } else {
+        ED_Invalidate (D);
+    }
+
+    /* In any case, the result is a zero page expression */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
+
+
+
+static void StudyByte2 (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_BYTE2 expression node */
+{
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* We can handle only const expressions */
+    if (ED_IsConst (D)) {
+        D->Val = (D->Val >> 16) & 0xFF;
+    } else {
+        ED_Invalidate (D);
+    }
+
+    /* In any case, the result is a zero page expression */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
+
+
+
+static void StudyByte3 (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_BYTE3 expression node */
+{
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* We can handle only const expressions */
+    if (ED_IsConst (D)) {
+        D->Val = (D->Val >> 24) & 0xFF;
+    } else {
+        ED_Invalidate (D);
+    }
+
+    /* In any case, the result is a zero page expression */
+    D->AddrSize = ADDR_SIZE_ZP;
+}
+
+
+
+static void StudyWord0 (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_WORD0 expression node */
+{
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* We can handle only const expressions */
+    if (ED_IsConst (D)) {
+        D->Val &= 0xFFFFL;
+    } else {
+        ED_Invalidate (D);
+    }
+
+    /* In any case, the result is an absolute expression */
+    D->AddrSize = ADDR_SIZE_ABS;
+}
+
+
+
+static void StudyWord1 (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_WORD1 expression node */
+{
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* We can handle only const expressions */
+    if (ED_IsConst (D)) {
+        D->Val = (D->Val >> 16) & 0xFFFFL;
+    } else {
+        ED_Invalidate (D);
+    }
+
+    /* In any case, the result is an absolute expression */
+    D->AddrSize = ADDR_SIZE_ABS;
+}
+
+
+
+static void StudyFarAddr (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_FARADDR expression node */
+{
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* We can handle only const expressions */
+    if (ED_IsConst (D)) {
+        D->Val &= 0xFFFFFFL;
+    } else {
+        ED_Invalidate (D);
+    }
+
+    /* In any case, the result is a far address */
+    D->AddrSize = ADDR_SIZE_FAR;
+}
+
+
+
+static void StudyDWord (ExprNode* Expr, ExprDesc* D)
+/* Study an EXPR_DWORD expression node */
+{
+    /* Study the expression */
+    StudyExprInternal (Expr->Left, D);
+
+    /* We can handle only const expressions */
+    if (ED_IsConst (D)) {
+        D->Val &= 0xFFFFFFFFL;
+    } else {
+        ED_Invalidate (D);
+    }
+
+    /* In any case, the result is a long expression */
+    D->AddrSize = ADDR_SIZE_LONG;
+}
 
-    /* Initialize SD. This is not needed in all cases, but it's rather cheap
-     * and simplifies the code below.
-     */
-    InitExprDesc (&SD);
 
+
+static void StudyExprInternal (ExprNode* Expr, ExprDesc* D)
+/* Study an expression tree and place the contents into D */
+{
     /* Study this expression node */
     switch (Expr->Op) {
 
-       case EXPR_LITERAL:
-            D->Val += (Sign * Expr->V.Val);
-           break;
-
-       case EXPR_SYMBOL:
-            Sym = Expr->V.Sym;
-            if (SymIsImport (Sym)) {
-                if (D->SymCount == 0) {
-                    D->SymCount += Sign;
-                    D->SymRef = Sym;
-                } else if (D->SymRef == Sym) {
-                    /* Same symbol */
-                    D->SymCount += Sign;
-                } else {
-                    /* More than one import */
-                    D->TooComplex = 1;
-                }
-            } else if (SymHasExpr (Sym)) {
-                if (SymHasUserMark (Sym)) {
-                    if (Verbosity > 0) {
-                        DumpExpr (Expr, SymResolve);
-                    }
-                    PError (GetSymPos (Sym),
-                            "Circular reference in definition of symbol `%s'",
-                            GetSymName (Sym));
-                    D->TooComplex = 1;
-                } else {
-                    SymMarkUser (Sym);
-                    StudyExpr (GetSymExpr (Sym), D, Sign);
-                    SymUnmarkUser (Sym);
-                }
-            } else {
-                D->TooComplex = 1;
-            }
+        case EXPR_LITERAL:
+            StudyLiteral (Expr, D);
             break;
 
-       case EXPR_SECTION:
-            Sec = Expr->V.SegNum;
-            if (D->SecCount == 0) {
-                D->SecCount += Sign;
-                D->SecRef = Sec;
-            } else if (D->SecRef == Sec) {
-                /* Same section */
-                D->SecCount += Sign;
-            } else {
-                /* More than one section */
-                D->TooComplex = 1;
-            }
-           break;
+        case EXPR_SYMBOL:
+            StudySymbol (Expr, D);
+            break;
 
-       case EXPR_ULABEL:
-            if (ULabCanResolve ()) {
-                /* We can resolve the label */
-                StudyExpr (ULabResolve (Expr->V.Val), D, Sign);
-            } else {
-                D->TooComplex = 1;
-            }
+        case EXPR_SECTION:
+            StudySection (Expr, D);
             break;
 
-       case EXPR_PLUS:
-                   StudyExpr (Expr->Left, D, Sign);
-                   StudyExpr (Expr->Right, D, Sign);
-           break;
+        case EXPR_ULABEL:
+            StudyULabel (Expr, D);
+            break;
+
+        case EXPR_PLUS:
+            StudyPlus (Expr, D);
+            break;
 
-       case EXPR_MINUS:
-           StudyExpr (Expr->Left, D, Sign);
-           StudyExpr (Expr->Right, D, -Sign);
-           break;
+        case EXPR_MINUS:
+            StudyMinus (Expr, D);
+            break;
 
         case EXPR_MUL:
-            InitExprDesc (&SD1);
-            StudyExpr (Expr->Left, &SD, 1);
-            StudyExpr (Expr->Right, &SD1, 1);
-            if (SD.TooComplex == 0 && SD1.TooComplex == 0) {
-                /* First calculate SD = SD*SD1 if possible */
-                if (ExprDescIsConst (&SD)) {
-                    /* Left is a constant */
-                    SD1.Val      *= SD.Val;
-                    SD1.SymCount *= SD.Val;
-                    SD1.SecCount *= SD.Val;
-                    SD = SD1;
-                } else if (ExprDescIsConst (&SD1)) {
-                    /* Right is constant */
-                    SD.Val      *= SD1.Val;
-                    SD.SymCount *= SD1.Val;
-                    SD.SecCount *= SD1.Val;
-                } else {
-                    D->TooComplex = 1;
-                }
-                /* Now calculate D * Sign * SD */
-                if (!D->TooComplex) {
-                    if ((D->SymCount == 0 || SD.SymCount == 0 || D->SymRef == SD.SymRef) &&
-                        (D->SecCount == 0 || SD.SecCount == 0 || D->SecRef == SD.SecRef)) {
-                        D->Val      += (Sign * SD.Val);
-                        if (D->SymCount == 0) {
-                            D->SymRef = SD.SymRef;
-                        }
-                        D->SymCount += (Sign * SD.SymCount);
-                        if (D->SecCount == 0) {
-                            D->SecRef = SD.SecRef;
-                        }
-                        D->SecCount += (Sign * SD.SecCount);
-                    }
-                } else {
-                    D->TooComplex = 1;
-                }
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyMul (Expr, D);
             break;
 
         case EXPR_DIV:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                if (SD.Val == 0) {
-                    Error ("Division by zero");
-                    D->TooComplex = 1;
-                } else {
-                    D->Val += Sign * (SD.Left / SD.Val);
-                }
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyDiv (Expr, D);
             break;
 
         case EXPR_MOD:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                if (SD.Val == 0) {
-                    Error ("Modulo operation with zero");
-                    D->TooComplex = 1;
-                } else {
-                    D->Val += Sign * (SD.Left % SD.Val);
-                }
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyMod (Expr, D);
             break;
 
         case EXPR_OR:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += Sign * (SD.Left | SD.Val);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyOr (Expr, D);
             break;
 
         case EXPR_XOR:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += Sign * (SD.Left ^ SD.Val);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyXor (Expr, D);
             break;
 
         case EXPR_AND:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += Sign * (SD.Left & SD.Val);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyAnd (Expr, D);
             break;
 
         case EXPR_SHL:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += (Sign * shl_l (SD.Left, (unsigned) SD.Val));
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyShl (Expr, D);
             break;
 
         case EXPR_SHR:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += (Sign * shr_l (SD.Left, (unsigned) SD.Val));
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyShr (Expr, D);
             break;
 
         case EXPR_EQ:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += Sign * (SD.Left == SD.Val);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyEQ (Expr, D);
             break;
 
         case EXPR_NE:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += Sign * (SD.Left != SD.Val);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyNE (Expr, D);
             break;
 
         case EXPR_LT:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += Sign * (SD.Left < SD.Val);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyLT (Expr, D);
             break;
 
         case EXPR_GT:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += Sign * (SD.Left > SD.Val);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyGT (Expr, D);
             break;
 
         case EXPR_LE:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += Sign * (SD.Left <= SD.Val);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyLE (Expr, D);
             break;
 
         case EXPR_GE:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += Sign * (SD.Left >= SD.Val);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyGE (Expr, D);
             break;
 
         case EXPR_BOOLAND:
-            StudyExpr (Expr->Left, &SD, 1);
-            if (ExprDescIsConst (&SD)) {
-                if (SD.Val != 0) {   /* Shortcut op */
-                    SD.Val = 0;
-                    StudyExpr (Expr->Right, &SD, 1);
-                    if (ExprDescIsConst (&SD)) {
-                        D->Val += Sign * (SD.Val != 0);
-                    } else {
-                        D->TooComplex = 1;
-                    }
-                }
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyBoolAnd (Expr, D);
             break;
 
         case EXPR_BOOLOR:
-            StudyExpr (Expr->Left, &SD, 1);
-            if (ExprDescIsConst (&SD)) {
-                if (SD.Val == 0) {   /* Shortcut op */
-                    StudyExpr (Expr->Right, &SD, 1);
-                    if (ExprDescIsConst (&SD)) {
-                        D->Val += Sign * (SD.Val != 0);
-                    } else {
-                        D->TooComplex = 1;
-                    }
-                } else {
-                    D->Val += Sign;
-                }
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyBoolOr (Expr, D);
             break;
 
         case EXPR_BOOLXOR:
-            StudyBinaryExpr (Expr, &SD);
-            if (!SD.TooComplex) {
-                D->Val += Sign * ((SD.Left != 0) ^ (SD.Val != 0));
-            }
+            StudyBoolXor (Expr, D);
+            break;
+
+        case EXPR_MAX:
+            StudyMax (Expr, D);
+            break;
+
+        case EXPR_MIN:
+            StudyMin (Expr, D);
             break;
 
         case EXPR_UNARY_MINUS:
-            StudyExpr (Expr->Left, D, -Sign);
+            StudyUnaryMinus (Expr, D);
             break;
 
         case EXPR_NOT:
-            StudyExpr (Expr->Left, &SD, 1);
-            if (ExprDescIsConst (&SD)) {
-                D->Val += (Sign * ~SD.Val);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyNot (Expr, D);
             break;
 
         case EXPR_SWAP:
-            StudyExpr (Expr->Left, &SD, 1);
-            if (ExprDescIsConst (&SD)) {
-                D->Val += Sign * (((SD.Val >> 8) & 0x00FF) | ((SD.Val << 8) & 0xFF00));
-            } else {
-                D->TooComplex = 1;
-            }
+            StudySwap (Expr, D);
             break;
 
         case EXPR_BOOLNOT:
-            StudyExpr (Expr->Left, &SD, 1);
-            if (ExprDescIsConst (&SD)) {
-                D->Val += Sign * (SD.Val != 0);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyBoolNot (Expr, D);
             break;
 
-        case EXPR_FORCEWORD:
-        case EXPR_FORCEFAR:
-            /* Ignore */
-            StudyExpr (Expr->Left, D, Sign);
+        case EXPR_BANK:
+            StudyBank (Expr, D);
             break;
 
         case EXPR_BYTE0:
-            StudyExpr (Expr->Left, &SD, 1);
-            if (ExprDescIsConst (&SD)) {
-                D->Val += Sign * (SD.Val & 0xFF);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyByte0 (Expr, D);
             break;
 
         case EXPR_BYTE1:
-            StudyExpr (Expr->Left, &SD, 1);
-            if (ExprDescIsConst (&SD)) {
-                D->Val += Sign * ((SD.Val >> 8) & 0xFF);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyByte1 (Expr, D);
             break;
 
         case EXPR_BYTE2:
-            StudyExpr (Expr->Left, &SD, 1);
-            if (ExprDescIsConst (&SD)) {
-                D->Val += Sign * ((SD.Val >> 16) & 0xFF);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyByte2 (Expr, D);
             break;
 
         case EXPR_BYTE3:
-            StudyExpr (Expr->Left, &SD, 1);
-            if (ExprDescIsConst (&SD)) {
-                D->Val += Sign * ((SD.Val >> 24) & 0xFF);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyByte3 (Expr, D);
             break;
 
         case EXPR_WORD0:
-            StudyExpr (Expr->Left, &SD, 1);
-            if (ExprDescIsConst (&SD)) {
-                D->Val += Sign * (SD.Val & 0xFFFF);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyWord0 (Expr, D);
             break;
 
         case EXPR_WORD1:
-            StudyExpr (Expr->Left, &SD, 1);
-            if (ExprDescIsConst (&SD)) {
-                D->Val += Sign * ((SD.Val >> 16) & 0xFFFF);
-            } else {
-                D->TooComplex = 1;
-            }
+            StudyWord1 (Expr, D);
+            break;
+
+        case EXPR_FARADDR:
+            StudyFarAddr (Expr, D);
+            break;
+
+        case EXPR_DWORD:
+            StudyDWord (Expr, D);
             break;
 
         default:
-           Internal ("Unknown Op type: %u", Expr->Op);
-           break;
+            Internal ("Unknown Op type: %u", Expr->Op);
+            break;
     }
 }
 
 
 
+void StudyExpr (ExprNode* Expr, ExprDesc* D)
+/* Study an expression tree and place the contents into D */
+{
+    unsigned I, J;
+
+    /* Call the internal function */
+    StudyExprInternal (Expr, D);
+
+    /* Remove symbol references with count zero */
+    I = J = 0;
+    while (I < D->SymCount) {
+        if (D->SymRef[I].Count == 0) {
+            /* Delete the entry */
+            --D->SymCount;
+            memmove (D->SymRef + I, D->SymRef + I + 1,
+                     (D->SymCount - I) * sizeof (D->SymRef[0]));
+        } else {
+            /* Next entry */
+            ++I;
+        }
+    }
+
+    /* Remove section references with count zero */
+    I = 0;
+    while (I < D->SecCount) {
+        if (D->SecRef[I].Count == 0) {
+            /* Delete the entry */
+            --D->SecCount;
+            memmove (D->SecRef + I, D->SecRef + I + 1,
+                     (D->SecCount - I) * sizeof (D->SecRef[0]));
+        } else {
+            /* Next entry */
+            ++I;
+        }
+    }
+
+    /* If we don't have an address size, assign one if the expression is a
+     * constant.
+     */
+    if (D->AddrSize == ADDR_SIZE_DEFAULT && ED_IsConst (D)) {
+        D->AddrSize = GetConstAddrSize (D->Val);
+    }
+
+    /* If the expression is valid, throw away the address size and recalculate
+     * it using the data we have. This is more exact than the on-the-fly
+     * calculation done when evaluating the tree, because symbols may have
+     * been removed from the expression, and the final numeric value is now
+     * known.
+     */
+    if (ED_IsValid (D)) {
+        unsigned char AddrSize;
+
+        /* If there are symbols or sections, use the largest one. If the
+         * expression resolves to a const, use the address size of the value.
+         */
+        if (D->SymCount > 0 || D->SecCount > 0) {
+
+            D->AddrSize = ADDR_SIZE_DEFAULT;
+
+            for (I = 0; I < D->SymCount; ++I) {
+                const SymEntry* Sym = D->SymRef[I].Ref;
+                AddrSize = GetSymAddrSize (Sym);
+                if (AddrSize > D->AddrSize) {
+                    D->AddrSize = AddrSize;
+                }
+            }
+
+            for (I = 0; I < D->SecCount; ++I) {
+                unsigned SegNum = D->SecRef[0].Ref;
+                AddrSize = GetSegAddrSize (SegNum);
+                if (AddrSize > D->AddrSize) {
+                    D->AddrSize = AddrSize;
+                }
+            }
+
+        } else {
+            AddrSize = GetConstAddrSize (D->Val);
+            if (AddrSize > D->AddrSize) {
+                D->AddrSize = AddrSize;
+            }
+        }
+    }
+
+#if 0
+    /* Debug code */
+    printf ("StudyExpr: "); DumpExpr (Expr, SymResolve);
+    printf ("Value: %08lX\n", D->Val);
+    if (!ED_IsValid (D)) {
+        printf ("Invalid: %s\n", AddrSizeToStr (D->AddrSize));
+    } else {
+        printf ("Valid:   %s\n", AddrSizeToStr (D->AddrSize));
+    }
+    printf ("%u symbols:\n", D->SymCount);
+    printf ("%u sections:\n", D->SecCount);
+#endif
+}