OpenCores
URL https://opencores.org/ocsvn/forwardcom/forwardcom/trunk

Subversion Repositories forwardcom

[/] [forwardcom/] [bintools/] [assem2.cpp] - Rev 166

Go to most recent revision | Compare with Previous | Blame | View Log

/****************************    assem2.cpp    ********************************
* Author:        Agner Fog
* Date created:  2017-04-17
* Last modified: 2021-08-11
* Version:       1.11
* Project:       Binary tools for ForwardCom instruction set
* Module:        assem.cpp
* Description:
* Module for assembling ForwardCom .as files. 
* This module contains:
* - expression(): Interpretation of expressions containing operators and 
*                 any type of operands.
* Copyright 2017-2021 GNU General Public License http://www.gnu.org/licenses
******************************************************************************/
#include "stdafx.h"
 
 
// Interpret and evaluate expression
SExpression CAssembler::expression(uint32_t tok1, uint32_t maxtok, uint32_t options) {
    // tok1: index to first token, 
    // maxtok: maximum number of tokens to use, 
    // options: 0: normal, 
    // 1: unsigned
    // 2: inside []. interpret as memory operand
    // 4: interpret option = keyword
    // 8: inside {}. has no meaning yet
    // 0x10: check syntax and count tokens, but do not call functions or report numeric 
    //       overflow, wrong operand types, or unknown names
 
    // This function scans the tokens and finds the operator with lowest priority. 
    // The function is called recursively for each operand to this operator.
    // The level of parantheses is saved in the brackets stack.
    // The scanning terminates at any of these conditions:
    // * a token that cannot be part of the expression is encountered
    // * all tokens are used
    // * a comma is encountered
    // * an unmatched end bracket is encountered
 
    uint32_t tok;                 // current token
    uint32_t toklow = tok1;       // operator with lowest priority
    uint32_t tokcolon = 0;        // matching triadic operator with lowest priority
    uint32_t ntok = 0;            // number of tokens used
    uint32_t priority = 0;        // priority of this operator
    uint32_t bracketlevel = 0;    // number of brackets in stack
    uint32_t state = 0;           // 0: expecting value, 1: after value, expecting operator or end
    uint32_t i;                   // loop counter
    uint32_t temp;                // temporary result
    uint32_t tokid;               // token.id
    int32_t  symi;                // symbol index
    bool     is_local = false;    // symbol is local constant
    uint8_t  endbracket;          // expected end bracket
 
    SExpression exp1, exp2;       // expressions during evaluation
    zeroAllMembers(exp1);         // reset exp1
    exp1.tokens = 1;
 
    for (tok = tok1; tok < tok1 + maxtok; tok++) {
        if (lineError) {exp1.etype = 0;  return exp1;}
        if (tokens[tok].type == TOK_OPR) {
            // operator found. search for brackets
            if (tokens[tok].priority == 1 || tokens[tok].priority == 14) {
                // bracket found. ?: operator treated as bracket here                
                switch (tokens[tok].id) {
                case '?':
                    if (tokens[tok].priority > priority && bracketlevel == 0) {  // if multiple ?:, split by the last one
                        priority = tokens[tok].priority; toklow = tok;
                    }
                    // continue in next case
                case '(': case '[': case '{':  // opening bracket. push on bracket stack
                    brackets.push(uint8_t(tokens[tok].id));
                    bracketlevel++;
                    state = 0;
                    break;
                case ')': case ']': case '}': case ':': // closing bracket
                    if (bracketlevel == 0) {                        
                        goto EXIT_LOOP;     // this end bracket is not part of the expression.
                    }
                    // remove matching opening bracket from stack
                    bracketlevel--;
                    endbracket = brackets.pop();
                    switch (endbracket) {
                    case '(':  endbracket = ')';  break;
                    case '[':  endbracket = ']';  break;
                    case '{':  endbracket = '}';  break;
                    case '?':  endbracket = ':';  break;
                    }
                    if (endbracket != tokens[tok].id) {
                        // end bracket does not match begin bracket
                        errors.report(tokens[tok].pos, tokens[tok].stringLength, ERR_BRACKET_END);
                        goto EXIT_LOOP;
                    }
                    if (tokens[tok].id == ':') {
                        if (bracketlevel == 0 && priority == 14 && tokcolon == 0) {
                            tokcolon = tok; // ':' matches current '?' with lowest priority
                        }
                        state = 0;
                        continue;
                    }
                    state = 1;
                    continue;  // finished with this token                
                }
            }
            if (bracketlevel) continue;  // don't search for priority inside brackets
 
            if (state == 1) {
                // expecting operator
                if (tokens[tok].id == ';') break;  // end at semicolon
                if (tokens[tok].id == ',' && !(options & 2)) break;  // end at comma, except inside []
                if (tokens[tok].id == '=' && !(options & 6)) break;  // end at =, except inside [] or when interpreting option = value
 
                if (tokens[tok].priority >= priority) {  // if multiple operators with same priority, split by the last one to get the first evaluated first
                    // operator with lower priority found
                    priority = tokens[tok].priority;
                    toklow = tok;
                }
                if (tokens[tok].priority == 3) state = 1; else state = 0;  // state 0 except after monadic operator
            }
            else if (state == 0 && (tokens[tok].id == '-' || tokens[tok].id == '+' || tokens[tok].priority == 3)) {
                // monadic operator
                if (priority < 3) {                
                    priority = 3;  toklow = tok;
                }
            }
            else {                
                errors.report(tokens[tok]);  break;  // unexpected operator
            }
        }
        else {
            // not an operator
            if (bracketlevel) continue;  // inside brackets: search only for end bracket
            if (state == 0) {
                // expecting value
                switch (tokens[tok].type) {
                case TOK_NAM: case TOK_LAB: case TOK_VAR: case TOK_SEC: 
                case TOK_NUM: case TOK_FLT: case TOK_CHA: case TOK_STR:
                case TOK_REG: case TOK_SYM: case TOK_XPR: case TOK_OPT:
                    state = 1; // allowed value tokens
                    break;
                case TOK_TYP:
                    state = 1; // type expression
                    break;
                case TOK_HLL:
                    if (tokens[tok].id == HLL_FALSE || tokens[tok].id == HLL_TRUE) {
                        state = 1; 
                    }
                    else {
                        errors.report(tokens[tok]);
                    }
                    break;
                default:
                    errors.report(tokens[tok]);  break;
                }
            }
            else {                
                break;    // no operator found after value. end here
            }
        }
    }
    EXIT_LOOP:
    if (lineError) {exp1.etype = 0;  return exp1;}
    // number of tokens used
    ntok = tok - tok1;
    exp1.tokens = ntok;
    if (bracketlevel) {
        endbracket = brackets.pop();
        errors.report(tokens[tok1].pos, tokens[tok].pos - tokens[tok1].pos, endbracket == '?' ? ERR_QUESTION_MARK : ERR_BRACKET_BEGIN);
        if (exp1.etype == 0) exp1.etype = XPR_INT;
        return exp1;
    }
    if (ntok == 0) {  // no expression found
        if (maxtok == 0 && tok > 0) tok--;
        errors.report(tokens[tok].pos, tokens[tok].stringLength, ERR_MISSING_EXPR);
        return exp1;
    }
 
    switch (priority) {
    case 0:  // no operator found. just an expression
        if (ntok > 2 && tokens[tok1].type == TOK_OPR && tokens[tok1].priority == 1) {
            // this is an expression in brackets
            uint32_t option1 = options;
            if (tokens[tok1].id == '[') {
                if (options & 2) errors.report(tokens[tok1]); // nested [[]] not allowed
                option1 |= 2;
            }
            if (tokens[tok1].id == '{') option1 |= 8;
            // evaluate expression inside bracket
            exp1 = expression(tok1 + 1, ntok - 2, option1);
            exp1.tokens += 2;
            goto RETURNEXP1;
        }
        else if (ntok == 1) {
            // this is a single token. get value
            switch (tokens[tok1].type) {
            case TOK_LAB: case TOK_VAR: case TOK_SEC: case TOK_SYM:
                exp1.etype = XPR_SYM1;            // symbol address
                exp1.sym3 = tokens[tok1].id;
                symi = findSymbol(exp1.sym3);
                // is symbol local with known value?
                is_local = symi > 0 && symbols[symi].st_bind == STB_LOCAL && (symbols[symi].st_type == STT_CONSTANT || symbols[symi].st_type == STT_VARIABLE);
                if (options & 2) {  // symbol inside []
                    exp1.etype |= XPR_MEM;
                    exp1.sym3 = 0;
                    if (is_local) {
                        exp1.offset_mem = tokens[tok1].value.w;// don't take value from symbol, it may change
                        exp1.etype &= ~XPR_SYM1;               // symbol reference no longer needed
                        exp1.etype |= XPR_OFFSET;              // has offset
                    }
                    else {
                        exp1.sym1 = tokens[tok1].id;
                    }
                    if (exp1.etype & (XPR_FLT | XPR_STRING)) { // float or string not allowed in memory operand
                        errors.report(tokens[tok1].pos, tokens[tok1].stringLength, ERR_WRONG_TYPE);
                    }
                }
                else {  // symbol outside []
                    if (is_local) {
                        if (symbols[symi].st_other & STV_FLOAT) exp1.etype |= XPR_FLT;
                        else exp1.etype |= XPR_INT;
                        exp1.value.i = tokens[tok1].value.u;   // don't take value from symbol, it may change
                        if (symbols[symi].st_other & STV_STRING) {
                            exp1.etype = XPR_STRING; 
                            exp1.sym2 = (uint32_t)symbols[symi].st_unitnum; // sym2 used for string length
                        }
                        else {
                            exp1.etype &= ~XPR_SYM1;            // symbol reference no longer needed
                            exp1.sym3 = 0;
                        }
                    }
                    else {                    
                        exp1.etype |= XPR_INT; // type not known yet?
                        exp1.sym3 = tokens[tok1].id;
                    }
                }
                break;
            case TOK_NUM:
                if (options & 2) { // number inside [] is offset
                    exp1.etype = XPR_OFFSET;  // integer value
                    exp1.offset_mem = (int32_t)interpretNumber((char*)buf()+tokens[tok1].pos, tokens[tok1].stringLength, &temp);
                }
                else {  // number outside [] is operand
                    exp1.etype = XPR_INT;  // integer value
                    exp1.value.i = interpretNumber((char*)buf() + tokens[tok1].pos, tokens[tok1].stringLength, &temp);
                }
                if (temp) errors.report(tokens[tok1]);
                break;
            case TOK_FLT:
                exp1.etype = XPR_FLT;  // floating point value
                exp1.value.d = interpretFloat((char*)buf()+tokens[tok1].pos, tokens[tok1].stringLength);
                if (options & 2) {   // float not allowed in memory operand
                    errors.report(tokens[tok1].pos, tokens[tok1].stringLength, ERR_WRONG_TYPE);
                }
                break;
            case TOK_CHA:  {          // character(s). convert to integer
                exp1.etype = XPR_INT; 
                exp1.value.u = 0;
                bool escape = false;  // check for \ escape characters
                int j = 0;            // count characters
                for (i = 0; i < tokens[tok1].stringLength; i++) {
                    uint8_t c = get<uint8_t>(tokens[tok1].pos + i);
                    if (c == '\\' && !escape) {
                        escape = true; continue;           // escape next character
                    }
                    if (escape) {   // special escape characters
                        switch (c) {
                        case '\\':            break;
                        case 'n':  c = '\n';  break;
                        case 'r':  c = '\r';  break;
                        case 't':  c = '\t';  break;
                        case '0':  c = 0;     break;
                        }
                    }
                    escape = false;
                    exp1.value.u += uint64_t(c) << j*8;
                    j++;
                }
                if (options & 2) {   // string not allowed in memory operand
                    errors.report(tokens[tok1].pos, tokens[tok1].stringLength, ERR_WRONG_TYPE);
                }
                break;}
            case TOK_STR:  {          // string
                exp1.etype = XPR_STRING;
                exp1.value.u = stringBuffer.dataSize();    // save position of string
                exp1.sym2 = tokens[tok1].stringLength;     // string length
                bool escape = false;                       // check for \ escape characters
                for (i = 0; i < tokens[tok1].stringLength; i++) {
                    char c = get<char>(tokens[tok1].pos + i);
                    if (c == '\\' && !escape) {
                        escape = true; continue;           // escape next character
                    }
                    if (escape) {   // special escape characters
                        switch (c) {
                        case '\\':  escape = false;  break;
                        case 'n':  c = '\n';  break;
                        case 'r':  c = '\r';  break;
                        case 't':  c = '\t';  break;
                        case '0':  c = 0;     break;
                        }
                    }
                    if (escape && exp1.sym2) exp1.sym2--;  // reduce length
                    stringBuffer.put(c);
                    escape = false;
                }
                stringBuffer.put(char(0));                 // terminate string
                if (options & 2) {                         // string not allowed in memory operand
                    errors.report(tokens[tok1].pos, tokens[tok1].stringLength, ERR_WRONG_TYPE);
                }
                break;}
            case TOK_REG:
                if (options & 2) { // register inside [] is base register
                    exp1.etype = XPR_BASE | XPR_MEM;
                    exp1.base = uint8_t(tokens[tok1].id);
                    if ((tokens[tok1].id & 0xFE0) == REG_SPEC) {
                        exp1.base = tokens[tok1].id >> 16;  // special register. to do: check if register type is valid
                    }
                }
                else {             // normal register operand
                    exp1.etype = XPR_REG | XPR_REG1;
                    exp1.reg1 = tokens[tok1].id;
                }
                break;
            case TOK_NAM:
                if ((options & 0x10) == 0) errors.report(tokens[tok1]);
                exp1.etype |= XPR_UNRESOLV;        // unresolved name
                break;
            case TOK_OPT:
                exp1.etype = XPR_OPTION;
                if ((tokens[tok1].id) == OPT_SCALAR) {
                    exp1.etype |= XPR_SCALAR;
                }
                else {
                    exp1.value.u = tokens[tok1].id;
                }
                break;
            case TOK_XPR:  // expression
                if (tokens[tok1].value.u < expressions.numEntries()) {
                    exp1 = expressions[tokens[tok1].value.w];
                    exp1.tokens = ntok;
                    if ((exp1.etype & XPR_REG) && !(exp1.etype & XPR_MEM) && (options & 2)) { // register inside [] is base register
                        exp1.etype = XPR_BASE | XPR_MEM;
                        exp1.base = exp1.reg1;
                        exp1.reg1 = 0;
                    }
                }
                else errors.report(tokens[tok1]);
                break;
            case TOK_TYP:
                exp1.etype = XPR_TYPENAME;
                exp1.value.u = tokens[tok1].id;
                break;
            case TOK_HLL:
                if (tokens[tok1].id == HLL_FALSE || tokens[tok1].id == HLL_TRUE) {  // translate to constant
                    exp1.etype = XPR_INT;
                    exp1.value.u = tokens[tok1].id & 1;
                }
                else {
                    errors.report(tokens[tok1]);
                }
                break;
            default:
                errors.report(tokens[tok1]);
            }
            if (options & 2) exp1.etype |= XPR_MEM;  // inside [], interpret as memory operand
            goto RETURNEXP1;
        }
        else {
            // unrecognized token
            errors.report(tokens[tok1]);
        }
        break;
 
    case 3:  // monadic operator
        if (toklow == tok1) {  // operator comes first
            exp1 = expression(toklow + 1, maxtok - 1, options);   // evaluate the rest
            if (exp1.etype & XPR_UNRESOLV) {
                exp1.tokens++;   // unresolved expression. return unresolved result
                goto RETURNEXP1;
            }
            zeroAllMembers(exp2);  // zero exp2
            switch (tokens[toklow].id) {
            case '+':            // value is unchanged
                exp1.tokens++;
                goto RETURNEXP1;;  // value is unchanged
            case '-':
                if (exp1.etype & (XPR_OP | XPR_REG | XPR_MEM)) {
                    exp1 = op1minus(exp1); // convert -(A+B) etc.
                    goto RETURNEXP1;
                }
                exp2 = exp1;      // convert -A to 0-A
                exp1.tokens = 0;
                exp1.etype = XPR_INT;
                exp1.value.i = 0;
                tokid = '-';
                break;  // continue in dyadic operators with 0-exp2
            case '!':
                exp1.tokens++;
                if (exp1.instruction == II_COMPARE
                && (exp1.etype & XPR_REG1) && (exp1.etype & (XPR_REG2 | XPR_INT | XPR_IMMEDIATE))) {
                    // compare instruction. invert condition
                    exp1.optionbits ^= 1;
                    exp1.etype |= XPR_OPTIONS;
                    if ((exp1.reg1 & REG_V) && (dataType & TYP_FLOAT)) exp1.optionbits ^= 8; // floating point compare. invert gives unordered
                    goto RETURNEXP1;
                }
                if (exp1.instruction == II_AND
                && (exp1.etype & XPR_REG1) && (exp1.etype & XPR_INT)) {
                    // test_bit/test_bits_or/jump instruction. invert condition
                    exp1.optionbits ^= 4;
                    exp1.etype |= XPR_OPTIONS;
                    goto RETURNEXP1;
                }
                if (exp1.instruction == II_TEST_BITS_AND && (exp1.etype & XPR_REG1) && (exp1.etype & XPR_INT)) {
                    // test_bits_and/jump instruction. invert condition
                    exp1.optionbits ^= 1;
                    exp1.etype |= XPR_OPTIONS;
                    goto RETURNEXP1;
                }
                if (exp1.etype & (XPR_MEM | XPR_REG)) { // '!' ambiguous on register and memory operands
                    errors.report(tokens[toklow].pos, tokens[toklow].stringLength, ERR_NOT_OP_AMBIGUOUS);
                }
                exp2.tokens = 0;
                exp2.etype = XPR_INT;
                exp2.value.i = 0;
                tokid = '='+D2;
                break;  // continue in dyadic operators with exp1 == 0
            case '~':
                exp2.tokens = 0;
                exp2.etype = XPR_INT;
                exp2.value.i = -1;
                tokid = '^';
                break;  // continue in dyadic operators with exp1 ^ -1
            default:
                errors.report(tokens[tok1]);  // ++ and -- not supported in expression
                return exp1;
            }
            goto DYADIC;   // proceed to dyadic operator
        }
        else {  // postfix ++ and --
            errors.report(tokens[tok1+1]);  // ++ and -- not supported in expression
        }
        goto RETURNEXP1;
 
    case 14: // triadic operator ?:
        // evaluate exp1 ? exp2 : exp3 for all expression types
        return op3(tok1, toklow, tokcolon, maxtok, options);
 
    default:; // continue below for dyadic operator
    }
    // dyadic operator. evaluate two subexpressions
    exp1 = expression(tok1, toklow - tok1, options);  // evaluate fist expression
    if (exp1.tokens != toklow - tok1) errors.report(tokens[tok1 + exp1.tokens]);
    if (lineError) return exp1;
 
    exp2 = expression(toklow + 1, tok1 + maxtok - (toklow + 1), options);  // evaluate second expression
    ntok = toklow - tok1 + 1 + exp2.tokens;
    tokid = tokens[toklow].id;  // operator id
    if (lineError) return exp1;
 
DYADIC:
    exp1 = op2(tokid, exp1, exp2);
 
    RETURNEXP1:
    if (lineError) return exp1;
    if (exp1.etype & XPR_ERROR) {
        errors.report(tokens[toklow].pos, tokens[toklow].stringLength, exp1.value.w);
    }
    return exp1;
}
 
// Interpret dyadic expression with any type of operands
SExpression CAssembler::op2(uint32_t op, SExpression & exp1, SExpression & exp2) {
 
    if ((exp1.etype | exp2.etype) & XPR_UNRESOLV) {
        exp1.etype = XPR_UNRESOLV;  // unresolved operand. make unresolved result
        exp1.tokens += exp2.tokens + 1;
    }
    else if ((exp1.etype & exp2.etype & XPR_MEM) 
        //&& ((exp1.etype|exp2.etype) & (XPR_BASE|XPR_INDEX|XPR_OPTION|XPR_SYM1|XPR_SYM2|XPR_LIMIT|XPR_LENGTH|XPR_BROADC))
        ) {
        exp1 = op2Memory(op, exp1, exp2);                // generation of memory operand. both operands inside [] and contain not only constants
    }
    else if (exp1.etype == XPR_OPTION && op == '=') {
        // option = value is handled by op2Memory
        exp1 = op2Memory(op, exp1, exp2);
    }
    else if (exp1.etype & exp2.etype & XPR_SYM1) {
        // adding or subtracting symbols and integers
        exp1 = op2Memory(op, exp1, exp2);
    } 
    else if ((exp1.etype & XPR_SYM2) && (exp2.etype & XPR_INT)) {
        // (sym1-sym2)/const
        exp1 = op2Memory(op, exp1, exp2);
    }
    // generation of instruction involving registers and/or memory operand:
    // (don't rely on XPR_MEM flag here because we would catch expressions involving constants only inside [] )
    //!else if ((exp1.etype | exp2.etype) & (XPR_REG | XPR_BASE /*| XPR_SYM1*/)) {
//    else if ((exp1.etype | exp2.etype) & (XPR_REG | XPR_BASE | XPR_SYM1)) {     //??
    else if (((exp1.etype | exp2.etype) & (XPR_REG | XPR_BASE)) || (exp1.sym1 | exp2.sym1)) {     //??
        exp1 = op2Registers(op, exp1, exp2);                
    }
    else if ((exp1.etype | exp2.etype) & XPR_STRING) {
        exp1 = op2String(op, exp1, exp2);             // string operation
    }
    else if ((exp1.etype & 0xF) == XPR_FLT || (exp2.etype & 0xF) == XPR_FLT) {
        // dyadic operators for float                 // floating point operation
        exp1 = op2Float(op, exp1, exp2);
    }
    else if ((exp1.etype & 0xF) == XPR_INT && (exp2.etype & 0xF) == XPR_INT) {
        // dyadic operators for integers              // integer operation
        exp1 = op2Int(op, exp1, exp2);
    }
    else {
        // other types
        exp1.etype = XPR_ERROR;
        exp1.value.u = ERR_WRONG_TYPE;
    }
    return exp1;
}
 
// Interpret dyadic expression with integer operands
SExpression CAssembler::op2Int(uint32_t op, SExpression const & exp1, SExpression const & exp2) {
    SExpression expr = exp1;
    expr.tokens = exp1.tokens + exp2.tokens + 1;
    switch (op & ~OP_UNS) {
    case '+':
        expr.value.u = exp1.value.u + exp2.value.u;
        break;
    case '-':
        expr.value.u = exp1.value.u - exp2.value.u;
        break;
    case '*':
        expr.value.i = exp1.value.i * exp2.value.i;
        break;
    case '/':
        if (exp2.value.i == 0) {
            expr.etype |= XPR_ERROR;
            expr.value.u = ERR_OVERFLOW;
            break;
        }
        if (op & OP_UNS) expr.value.u = exp1.value.u / exp2.value.u; // unsigned division
        else  expr.value.i = exp1.value.i / exp2.value.i; // signed division
        break;
    case '%':
        if (exp2.value.i == 0) {
            expr.etype |= XPR_ERROR;
            expr.value.u = ERR_OVERFLOW;
            break;
        }
        if (op & OP_UNS) expr.value.u = exp1.value.u % exp2.value.u; // unsigned modulo
        else  expr.value.i = exp1.value.i % exp2.value.i; // signed modulo
        break;
    case '<' + D2:  // <<
        expr.value.u = exp1.value.u << exp2.value.u;
        break;
    case '>' + D2:  // >>  shift right signed
        if (op & OP_UNS) expr.value.u = exp1.value.u >> exp2.value.u; // unsigned shift right
        else  expr.value.i = exp1.value.i >> exp2.value.i; // signed shift right
        break;
    case '>' + D3:  // >>> unsigned shift right
        expr.value.u = exp1.value.u >> exp2.value.u;
        break;
    case '<':    // < compare
        if (op & OP_UNS) expr.value.i = exp1.value.u < exp2.value.u; // unsigned compare
        else  expr.value.i = exp1.value.i < exp2.value.i; // signed compare
        break;
    case '<' + EQ:  // <=  compare
        if (op & OP_UNS) expr.value.i = exp1.value.u <= exp2.value.u; // unsigned compare
        else  expr.value.i = exp1.value.i <= exp2.value.i; // signed compare
        break;
    case '>':    // > compare
        if (op & OP_UNS) expr.value.i = exp1.value.u > exp2.value.u; // unsigned compare
        else  expr.value.i = exp1.value.i > exp2.value.i; // signed compare
        break;
    case '>' + EQ:   // >= compare
        if (op & OP_UNS) expr.value.i = exp1.value.u >= exp2.value.u; // unsigned compare
        else  expr.value.i = exp1.value.i >= exp2.value.i; // signed compare
        break;
    case '=' + D2:   // ==
        expr.value.u = exp1.value.u == exp2.value.u;
        break;
    case '!' + EQ:   // !=
        expr.value.u = exp1.value.u != exp2.value.u;
        break;
    case '&':  // bitwise and
        expr.value.u = exp1.value.u & exp2.value.u;
        break;
    case '|':  // bitwise or
        expr.value.u = exp1.value.u | exp2.value.u;
        break;
    case '^':  // bitwise xor
        expr.value.u = exp1.value.u ^ exp2.value.u;
        break;
    case '&' + D2:  // logical and
        expr.value.u = exp1.value.u && exp2.value.u;
        break;
    case '|' + D2:  // logical or
        expr.value.u = exp1.value.u || exp2.value.u;
        break;
    case '^' + D2:  // logical xor
        expr.value.u = (exp1.value.u != 0) ^ (exp2.value.u != 0);
        break;
    default:  // unsupported operator
        expr.etype |= XPR_ERROR;
        expr.value.u = ERR_WRONG_TYPE;
    }
    return expr;
}
 
// Interpret dyadic expression with floating point operands
SExpression CAssembler::op2Float(uint32_t op, SExpression & exp1, SExpression & exp2) {
    SExpression expr = exp1;
    expr.tokens = exp1.tokens + exp2.tokens + 1;
    if (exp1.etype == XPR_INT) {  // convert exp1 to float
        exp1.value.d = (double)exp1.value.i;
        expr.etype = XPR_FLT;
    }
    if (exp2.etype == XPR_INT) {  // convert exp2 to float
        exp2.value.d = (double)exp2.value.i;
        expr.etype = XPR_FLT;
    }
    // dyadic operator on float
    switch (op) {
    case '+':
        expr.value.d = exp1.value.d + exp2.value.d;
        break;
    case '-':
        expr.value.d = exp1.value.d - exp2.value.d;
        break;
    case '*':
        expr.value.d = exp1.value.d * exp2.value.d;
        break;
    case '/':
        if (exp2.value.d == 0.) {
            expr.etype |= XPR_ERROR;
            expr.value.u = ERR_OVERFLOW;
            break;
        }
        expr.value.d = exp1.value.d / exp2.value.d;
        break;
    case '<':    // signed compare
        expr.value.i = exp1.value.d < exp2.value.d;
        expr.etype = XPR_INT;
        break;
    case '<' + EQ:  // <= signed compare
        expr.value.i = exp1.value.d <= exp2.value.d;
        expr.etype = XPR_INT;
        break;
    case '>':    // signed compare
        expr.value.i = exp1.value.d > exp2.value.d;
        expr.etype = XPR_INT;
        break;
    case '>' + EQ:   // >= signed compare
        expr.value.i = exp1.value.d <= exp2.value.d;
        expr.etype = XPR_INT;
        break;
    case '=' + D2:   // ==
        expr.value.i = exp1.value.d == exp2.value.d;
        expr.etype = XPR_INT;
        break;
    case '!' + EQ:   // !=
        expr.value.i = exp1.value.d != exp2.value.d;
        expr.etype = XPR_INT;
        break;
    case '&' + D2:  // logical and
        expr.value.i = exp1.value.d != 0. && exp2.value.d != 0.;
        expr.etype = XPR_INT;  break;
        break;
    case '|' + D2:  // logical or
        expr.value.i = exp1.value.d != 0. || exp2.value.d != 0.;
        expr.etype = XPR_INT;  break;
        break;
    default:  // unsupported operator
        expr.etype |= XPR_ERROR;
        expr.value.u = ERR_WRONG_TYPE;
    }
    return expr;
}
 
// Interpret dyadic expression with register or memory operands, generating instruction
SExpression CAssembler::op2Registers(uint32_t op, SExpression const & ex1, SExpression const & ex2) {
    SExpression expr = {{0}};         // return expression
    uint8_t swapped = false;          // operands are swapped
    uint8_t cannotSwap = false;       // cannot swap operands because both contain vector registers
    uint32_t i;                       // loop counter
 
                                      // make array of the two expressions
    SExpression exp12[2];             // copy of expressions
    uint32_t numtokens = ex1.tokens + ex2.tokens + 1; // number of tokens
    expr.tokens = numtokens;
 
    // resolve nested expressions
    if ((ex1.etype | ex2.etype) & XPR_OP) {
        /*
        if (op == '&' && (ex1.etype & XPR_REG) && !(ex1.etype & XPR_OP) && ex2.instruction == II_XOR && ((ex2.etype & 0xF) == XPR_INT) && ex2.value.i == -1) {
            // A & (B ^ -1) = and_not(A,B). This instruction is removed
            expr = ex1;  expr.tokens = numtokens;
            expr.etype |= XPR_OP;
            expr.instruction = II_AND_NOT;
            expr.reg2 = ex2.reg1;
            return expr;
        } */
        // simplify both expressions if possible
        exp12[0] = ex1;  exp12[1] = ex2; 
        for (i = 0; i < 2; i++) {
            if ((exp12[i].etype & (XPR_REG | XPR_MEM)) && (exp12[i].etype & XPR_IMMEDIATE) && exp12[i].value.i == 0) {
                if (exp12[i].instruction == II_SUB_REV) {
                    // expression is -A converted to (0-A). change to register and sign bit
                    exp12[i].etype &= ~(XPR_OPTIONS | XPR_IMMEDIATE | XPR_OP);
                    exp12[i].instruction = 0;
                    exp12[i].optionbits = 1;
                }
                else if (exp12[i].instruction == II_MUL_ADD && exp12[i].value.i == 0) {
                    // expression is -A*B converted to (-A*B+0). change to A*B and sign bit
                    exp12[i].instruction = II_MUL;
                    exp12[i].optionbits = exp12[i].optionbits & 1;
                    exp12[i].etype &= ~(XPR_OPTIONS | XPR_IMMEDIATE);
                }
                else if (exp12[i].instruction == II_ADD_ADD && (exp12[i].etype & (XPR_INT | XPR_FLT)) && (exp12[i].optionbits & 3) == 3 && exp12[i].value.i == 0) {
                    // expression is -(A+B) converted to (-A-B+0). change to A+B and sign bit
                    exp12[i].etype &= ~(XPR_INT | XPR_FLT);
                    exp12[i].instruction = II_ADD;
                    exp12[i].optionbits ^= 3;
                    exp12[i].etype &= ~(XPR_OPTIONS | XPR_IMMEDIATE);
                }
            }
            else if (exp12[i].instruction == II_SUB_REV) {
                // change -A+B to -(A-B)
                exp12[i].instruction = II_SUB;
                exp12[i].optionbits ^= 3;
            }
        }
        if ((exp12[0].etype & XPR_IMMEDIATE) && (exp12[1].etype & XPR_IMMEDIATE)) {
            // both operands contain an immediate. combine the immediates if possible
 
            bool isfloat[2];  // check if operands are float
            for (i = 0; i < 2; i++) isfloat[i] = (exp12[i].etype & XPR_IMMEDIATE) == XPR_FLT; 
 
            // convert integer to float if the other operand is float
            for (i = 0; i < 2; i++) {
                if (isfloat[1-i] && !isfloat[i]) {
                    exp12[i].value.d = (double)exp12[i].value.i;
                    isfloat[i] = true;
                }
            }
 
            if (op == '+' || op == '-') { // add or subtract operands and store in exp12[1]
                uint8_t sign = 0;  
                switch (exp12[0].instruction) {
                case II_ADD: case II_SUB_REV:
                    sign = exp12[0].optionbits >> 1 & 1;
                    if (op == '-') sign ^= 1;
                    break;
                case II_SUB:
                    sign = (exp12[0].optionbits >> 1 & 1) ^ 1;
                    if (op == '-') sign ^= 1;
                    break;
                case II_ADD_ADD:
                    sign = exp12[0].optionbits >> 2 & 1;
                    if (op == '-') sign ^= 1;
                    break;
                default:   // no other instructions can be combined with + or -
                    expr.etype |= XPR_ERROR; expr.value.u = ERR_WRONG_OPERANDS;
                    return expr;
                }
                if (exp12[1].instruction == II_SUB) sign ^= 1;
 
                // add immediates and store them in exp12[1]
                if (sign) {
                    if (isfloat[1]) exp12[1].value.d -= exp12[0].value.d;
                    else exp12[1].value.i -= exp12[0].value.i;
                }
                else {
                    if (isfloat[1]) exp12[1].value.d += exp12[0].value.d;
                    else exp12[1].value.i += exp12[0].value.i;
                }
                exp12[0].value.i = 0;
                exp12[0].etype &= ~ (XPR_INT | XPR_FLT);
                if (exp12[0].instruction == II_ADD_ADD) {
                    exp12[0].instruction = II_ADD; 
                    exp12[0].optionbits &= ~ 4;
                }
                else {
                    exp12[0].instruction = 0;
                }
            }
            else if (op == '*' && exp12[0].instruction == II_MUL) {
                if (isfloat[0]) {                
                    exp12[1].value.d *= exp12[0].value.d;
                }
                else {
                    exp12[1].value.i *= exp12[0].value.i;
                }
                exp12[0].value.i = 0;
                exp12[0].etype &= ~ (XPR_INT | XPR_FLT | XPR_OP);
                exp12[0].instruction = 0;
            } /*
            else if (op == '&' && exp12[0].instruction == II_AND && !isfloat[0]) {
                exp12[1].value.i &= exp12[0].value.i;
                exp12[0].value.i = 0;
                exp12[0].etype &= ~ XPR_INT;
                exp12[0].instruction = 0;
            }
            else if (op == '|' && exp12[0].instruction == II_OR && !isfloat[0]) {
                exp12[1].value.i |= exp12[0].value.i;
                exp12[0].value.i = 0;
                exp12[0].etype &= ~ XPR_INT;
                exp12[0].instruction = 0;
            }
            else if (op == '^' && exp12[0].instruction == II_XOR && !isfloat[0]) {
                exp12[1].value.i ^= exp12[0].value.i;
                exp12[0].value.i = 0;
                exp12[0].etype &= ~ XPR_INT;
                exp12[0].instruction = 0;
            } */
            else {
                expr.etype |= XPR_ERROR; expr.value.u = ERR_WRONG_OPERANDS;
            }
        }
 
        // error if two memory operands 
        uint32_t etyp0 = exp12[0].etype, etyp1 = exp12[1].etype;
        //if ((etyp0 &  etyp1 & XPR_MEM) || (exp12[0].value.i && exp12[1].value.i)) {
        if (etyp0 & etyp1 & XPR_MEM) {
            expr.etype |= XPR_ERROR; expr.value.u = ERR_WRONG_OPERANDS;
            return expr;
        }
 
        // error if too many operands
        if (((etyp0 & XPR_REG1) != 0) + ((etyp0 & XPR_REG2) != 0) + ((etyp0 & XPR_REG3) != 0)
            + ((etyp1 & XPR_REG1) != 0) + ((etyp1 & XPR_REG2) != 0) + ((etyp1 & XPR_REG3) != 0)
            + (((etyp0 | etyp1) & XPR_MEM) != 0) + (((etyp0 | etyp1) & XPR_IMMEDIATE) != 0) > 3) {
            expr.etype |= XPR_ERROR; expr.value.u = ERR_TOO_MANY_OPERANDS;
            return expr;
        } 
 
        // check which operations can swap
        if (op != '+' && op != '*' && op != '&' && op != '|' && op != '^' && op != '-') {
            cannotSwap = true;  // operation is not commutative ('-' is handled with sign bits)
        } 
 
        // put operands in this order: register, memory, immediate
        if ((exp12[0].etype & (XPR_IMMEDIATE | XPR_MEM)) && !(exp12[1].etype & XPR_IMMEDIATE) && !cannotSwap) {
            // first operand is immediate or memory, and second operant is not immediate
            // swap operands if not two vector registers
            if (exp12[0].reg1 & exp12[1].reg1 & REG_V) {                
                // both operands contain a vector register. cannot swap. make error message later if swapping required
                cannotSwap = true;
            }
            else if ((exp12[1].etype & XPR_MEM) && op == '*') {
                // second operand also contains memory
                cannotSwap = true;
            }
            else {  // swap operands to get immediate or memory operand last
                expr = exp12[0];  exp12[0] = exp12[1];  exp12[1] = expr;
                if (op == '-') {
                    op = '+';  // convert '-' to '+' and flip sign bit to make operation commutative
                    exp12[0].optionbits ^= 1;
                }
                swapped = true;
            }
        } 
 
        if (op == '+' || op == '-') {
            /* done above:
            if (exp12[0].etype & (XPR_IMMEDIATE | XPR_MEM) && exp12[1].instruction == II_MUL && !(exp12[1].etype & (XPR_INT | XPR_FLT))) {
                // (memory or constant) + reg*reg. swap operands
                expr = exp12[0];  exp12[0] = exp12[1];  exp12[1] = expr;
                if (op == '-') {
                    exp12[0].optionbits ^= 1;  // invert signs in both operands
                    exp12[1].optionbits ^= 1;
                }
            } */
            if (!((exp12[0].etype | exp12[1].etype) & XPR_OP)) {
                // +/-R1 +/-R2
                if (op == '-') exp12[1].optionbits ^= 1;   // sign of second operand
                // change sign of constant if this simplifies it
                if ((exp12[1].etype & XPR_INT) && (exp12[1].optionbits & 1)) {
                    exp12[1].value.i = -exp12[1].value.i;
                    exp12[1].optionbits = 0;
                }
                else if ((exp12[1].etype & XPR_FLT) && (exp12[1].optionbits & 1)) {
                    exp12[1].value.d = -exp12[1].value.d;
                    exp12[1].optionbits = 0;
                }
                uint8_t s = exp12[0].optionbits | exp12[1].optionbits << 1;  // combine signs
                expr = exp12[1];  expr.tokens = numtokens;
                expr.reg1 = exp12[0].reg1;
                if (exp12[1].etype & XPR_REG1) {                
                    expr.reg2 = exp12[1].reg1;  expr.etype |= XPR_REG2;
                }
                expr.etype |= XPR_OP | XPR_REG1;
                expr.optionbits = 0;
                switch (s) {
                case 0:  // R1 + R2
                    expr.instruction = II_ADD;  break;
                case 1:  // -R1 + R2
                    expr.instruction = II_SUB_REV;  break;
                case 2:  // R1 - R2
                    expr.instruction = II_SUB;  break;
                case 3:  // -R1 -R2
                    expr.instruction = II_ADD_ADD;
                    expr.value.i = 0;
                    expr.optionbits = s;
                    expr.etype |= XPR_INT | XPR_OPTIONS;
                    break;
                }
                return expr;
            }
            else if (exp12[0].instruction == II_MUL || exp12[1].instruction == II_MUL) {
                // (A*B)+C
                if (op == '-') exp12[1].optionbits ^= 1;  // change sign if '-'
                if (exp12[1].instruction == II_MUL) {     // swap expressions if A+(B*C)
                    if (exp12[0].reg1 & REG_V) {
                        expr.etype |= XPR_ERROR;
                        expr.value.w = ERR_CANNOT_SWAP_VECT; // cannot put vector addend as first operand
                        return expr;
                    }
                    expr = exp12[0]; exp12[0] = exp12[1]; exp12[1] = expr; // swap expressions
                } 
                expr = exp12[0] | exp12[1];  // combine expressions
                expr.tokens = numtokens;
                if ((exp12[0].etype & exp12[1].etype & (XPR_MEM|XPR_IMMEDIATE)) || // two memory or two immediate operands
                ((exp12[0].etype & (XPR_MEM|XPR_IMMEDIATE)) == (XPR_MEM|XPR_IMMEDIATE))) { // exp12[0] has both memory and immediate
                    expr.etype |= XPR_ERROR;
                    expr.value.w = ERR_TOO_COMPLEX;
                    return expr;
                }
                expr.instruction = II_MUL_ADD;
                expr.etype |= XPR_OPTIONS;
                if (((exp12[0].etype & XPR_MEM) && !(exp12[1].etype & XPR_IMMEDIATE)) || (exp12[0].etype & XPR_IMMEDIATE)) {
                    expr.instruction = II_MUL_ADD2;   // get A*C+B
                    // we don't need to do anything with signs here because the sign options apply to product and addend, not to specific operands
                }
                expr.etype |= XPR_OP;
                expr.reg1 = exp12[0].reg1;  expr.reg2 = exp12[0].reg2;  
                if (exp12[1].etype & XPR_REG) {  // C has a register
                    if (exp12[0].etype & XPR_REG2) {  // 3 registers
                        expr.reg3 = exp12[1].reg1;
                        expr.etype |= XPR_REG3;
                    }
                    else {
                        expr.reg2 = exp12[1].reg1;    // 2 registers
                        expr.etype |= XPR_REG2;
                    }
                } 
                // optionbits 0-1 = sign of product. optionbits 2-3 = sign of addend. 
                expr.optionbits = 3 * (exp12[0].optionbits & 1) | 0xC * (exp12[1].optionbits & 1);
                expr.etype |= XPR_OPTIONS;
                return expr;
            }
            else if (exp12[0].instruction == II_ADD || exp12[0].instruction == II_SUB) {
                // (A+B)+C
                expr = exp12[0] | exp12[1];  // combine expressions 
                expr.tokens = numtokens;
                expr.reg1 = exp12[0].reg1;
                expr.etype |= XPR_OP;
                expr.instruction = II_ADD_ADD;
 
                if ((exp12[0].etype & XPR_IMMEDIATE) || ((exp12[0].etype & XPR_MEM) && !(exp12[1].etype & XPR_IMMEDIATE))) {
                    // does not fit
                    expr.etype |= XPR_ERROR;
                    expr.value.w = cannotSwap ? ERR_CANNOT_SWAP_VECT : ERR_TOO_COMPLEX;
                    return expr;
                }
 
                if (exp12[1].etype & XPR_REG) {  // C has a register
                    if (exp12[0].etype & XPR_REG2) {  // 3 registers
                        expr.reg3 = exp12[1].reg1;
                        expr.etype |= XPR_REG3;
                    }
                    else if (exp12[0].etype & XPR_REG1) {  // 2 registers
                        expr.reg2 = exp12[1].reg1;
                        expr.etype |= XPR_REG2;
                    }
                    else {
                        expr.reg1 = exp12[1].reg1;    // 1 registers
                        expr.etype |= XPR_REG1;
                    }
                }
                expr.optionbits = (exp12[0].optionbits & 3) | ((exp12[1].optionbits & 1) ^ (op == '-')) << 2;
                if (exp12[0].instruction == II_SUB) expr.optionbits ^= 2;                    
                if (swapped && op == '-') expr.optionbits ^= 7;
                expr.etype |= XPR_OPTIONS;
                return expr;
 
            }
            else if (exp12[1].instruction == II_ADD || exp12[1].instruction == II_SUB) {
                // A+(B+C)
                expr = exp12[0] | exp12[1];  // combine expressions 
                expr.tokens = numtokens;
                expr.reg1 = exp12[0].reg1;
                expr.etype |= XPR_OP;
                expr.instruction = II_ADD_ADD;
 
                if (exp12[0].etype & exp12[1].etype & (XPR_IMMEDIATE | XPR_MEM)) {
                    // does not fit
                    expr.etype |= XPR_ERROR;
                    expr.value.w = ERR_TOO_COMPLEX;
                    return expr;
                }
 
                if (exp12[0].etype & XPR_MEM) {
                    // A = mem, B = register, C = immediate. Needs additional reordering
                    expr.optionbits = ((exp12[1].optionbits & 1) ^ (op == '-'))   // register into first place
                        | (exp12[0].optionbits & 1) << 1                          // memory in second place
                        | ((exp12[1].optionbits >> 1 & 1) ^ (op == '-')) << 2;     // immediate in third place
                    if (exp12[1].instruction == II_SUB) expr.optionbits ^= 4;
                    if (swapped && op == '-') expr.optionbits ^= 7;
                    expr.reg1 = exp12[1].reg1;
                    expr.etype |= XPR_OPTIONS;
                    return expr;
                }
 
 
                if (exp12[1].etype & XPR_REG2) {
                    // 3 registers
                    expr.reg2 = exp12[1].reg1;
                    expr.reg3 = exp12[1].reg2;
                    expr.etype |= XPR_REG2 | XPR_REG3;
                }
                else if (exp12[1].etype & XPR_REG1) {
                    // 2 registers
                    expr.reg2 = exp12[1].reg1;
                    expr.etype |= XPR_REG2;
                }
 
                expr.optionbits = (exp12[0].optionbits & 1) | 6 * ((exp12[1].optionbits & 1) ^ (op == '-'));
                if (exp12[1].instruction == II_SUB) expr.optionbits ^= 4;
                if (swapped && op == '-') expr.optionbits ^= 7;
                expr.etype |= XPR_OPTIONS;
                return expr;
            }
        }
        else if (!((exp12[0].etype | exp12[1].etype) & XPR_OP) 
            && (op == '*' || (op == '/' && !swapped))) { 
            // (+/- a) * (+/- b)
            expr = exp12[0] | exp12[1];
            expr.etype |= XPR_OP;
            expr.tokens = numtokens;
            expr.optionbits = exp12[0].optionbits ^ exp12[1].optionbits;
            if (expr.optionbits & 1) {   // change sign
                if ((exp12[1].etype & 0xF) == XPR_FLT) {
                    expr.value.d = -exp12[1].value.d;
                    expr.optionbits = 0;
                }
                else if ((exp12[1].etype & 0xF) == XPR_INT) {
                    expr.value.i = -exp12[1].value.i;
                    expr.optionbits = 0;
                }
                else if (/*(exp12[1].etype & XPR_REG) &&*/ op == '*' && expr.value.i == 0) {  
                    // change -a*b to -a*b + 0
                    expr.instruction = II_MUL_ADD;
                    expr.optionbits = 0x3;
                    expr.reg1 = exp12[0].reg1;
                    if (exp12[1].etype & XPR_REG1) {                    
                        expr.reg2 = exp12[1].reg1;  expr.etype |= XPR_REG2;
                    }
                    expr.etype |= XPR_INT | XPR_OPTIONS;
                    return expr;
                }
                else {
                    expr.etype |= XPR_ERROR; expr.value.w = ERR_TOO_COMPLEX;
                    return expr;
                }
            }
            expr.reg1 = exp12[0].reg1;
            if (exp12[1].etype & XPR_REG1) {            
                expr.reg2 = exp12[1].reg1;  expr.etype |= XPR_REG2;
            }
            expr.instruction = (op == '*') ? II_MUL : II_DIV;
            return expr;
        } 
 
        else if (((exp12[0].etype & exp12[1].etype) & XPR_INT) 
        && (op == '='+D2 || op == '!'+EQ)
        && exp12[0].value.i == exp12[1].value.i 
        && ((exp12[0].etype | exp12[1].etype) & (XPR_REG1 | XPR_REG2)) == XPR_REG1
        && (exp12[0].etype & exp12[1].etype & XPR_REG1) == 0) {
            // (r1 & const) == const gives test_bits_and
            expr = exp12[0] | exp12[1];
            expr.etype |= XPR_OP | XPR_OPTIONS;
            expr.tokens = numtokens;
            expr.instruction = II_TEST_BITS_AND;
            if (op == '!'+EQ) expr.optionbits ^= 1;             
            return expr;
        }
        else if (op == '&'+D2 || op == '|'+D2 || op == '^' || op == '^'+D2) {
            // possible combination of compare or test with extra boolean operand
            int swap = exp12[1].instruction != 0;
            expr = exp12[swap];
            if (expr.instruction == II_COMPARE && exp12[1-swap].etype == (XPR_REG | XPR_REG1)) {
                // use fallback register as an extra boolean operand on compare instruction
                switch (op & 0xFF) {
                case '&':
                    expr.optionbits |= 0x10;  break;
                case '|':
                    expr.optionbits |= 0x20;  break;
                case '^':
                    expr.optionbits |= 0x30;  break;
                default:
                    expr.etype |= XPR_ERROR; expr.value.u = ERR_TOO_COMPLEX;
                }
                expr.etype |= XPR_OP | XPR_OPTIONS | XPR_FALLBACK;
                expr.tokens = numtokens;
                expr.fallback = exp12[1-swap].reg1;
                return expr;
            }
            /*else if (expr.instruction >= II_TEST_BIT && expr.instruction <= II_TEST_BITS_OR && exp12[1-swap].etype == (XPR_REG | XPR_REG1)) {
                // Use fallback register as an extra boolean operand on bit test instructions
                // This does not work yet. test_bit cannot be expressed with high level operators
                switch (op & 0xFF) {
                case '&':
                    expr.optionbits |= 0x01;  break;
                case '|':
                    expr.optionbits |= 0x02;  break;
                case '^':
                    expr.optionbits |= 0x03;  break;
                default:
                    expr.etype |= XPR_ERROR; expr.value.u = ERR_TOO_COMPLEX;
                }
                expr.etype |= XPR_OP | XPR_OPTIONS | XPR_FALLBACK;
                expr.tokens = numtokens;
                expr.fallback = exp12[1-swap].reg1;
                return expr;
            }*/
        }
    }
 
    // not a complex expression
    if ((ex1.etype & (XPR_IMMEDIATE | XPR_MEM)) && !((ex1.reg1 & REG_V) || (ex2.etype & XPR_IMMEDIATE))){
        // first operand is integer, float or memory. swap operands if not two vector registers or memory and immediate
        exp12[0] = ex2;  exp12[1] = ex1;  swapped = true;
    }
    else {
        exp12[0] = ex1;  exp12[1] = ex2;  
    }
    expr.etype |= (exp12[1].etype & XPR_REG1) << 1;  // XPR_REG1 becomes XPR_REG2
 
    // combine everything from the two operands
    expr = exp12[0] | exp12[1];
    expr.etype |= XPR_OP;
    expr.tokens = numtokens;
    expr.reg1 = exp12[0].reg1;
    expr.reg2 = exp12[1].reg1;
    expr.etype |= (exp12[1].etype & XPR_REG1) << 1;
 
    if (expr.instruction) {
        expr.etype |= XPR_ERROR; expr.value.u = ERR_TOO_COMPLEX;
        return expr;
    } 
    // 2-operand instruction
    switch (op) {
    case '+':
        expr.instruction = II_ADD;  break;
    case '-':
        expr.instruction = swapped ? II_SUB_REV : II_SUB;  break;
    case '*':
        expr.instruction = II_MUL;  break;
    case '/':
        expr.instruction = swapped ? II_DIV_REV : II_DIV;  break;
    case '%':
        if (swapped) {expr.etype |= XPR_ERROR; expr.value.u = ERR_WRONG_TYPE;}
        expr.instruction = II_REM;  break;
    case '&':  case '&'+D2:  // boolean AND and bitwise AND have same implementation
        expr.instruction = II_AND;  break;
    case '|':  case '|'+D2:  // boolean OR and bitwise OR have same implementation
        expr.instruction = II_OR;  break;
    case '^':  case '^'+D2:
        expr.instruction = II_XOR;  break;
    case '<':
        expr.instruction = II_COMPARE;
        expr.optionbits = 2 ^ swapped;  
        expr.etype |= XPR_OPTIONS;
        break;
    case '<' + EQ:    // <=
        expr.instruction = II_COMPARE;  
        expr.optionbits = 5 ^ swapped;  
        expr.etype |= XPR_OPTIONS;
        break;
    case '>':
        expr.instruction = II_COMPARE;  
        expr.optionbits = 4 ^ swapped;  
        expr.etype |= XPR_OPTIONS;
        break;
    case '>' + EQ:   // >=
        expr.instruction = II_COMPARE;  
        expr.optionbits = 3 ^ swapped;  
        expr.etype |= XPR_OPTIONS;
        break;
    case '='+D2:  // ==
        expr.instruction = II_COMPARE;  
        expr.optionbits = 0;  
        //expr.etype |= XPR_OPTIONS;
        break;
    case '!'+EQ:  // !=
        expr.instruction = II_COMPARE;  
        expr.etype |= XPR_OPTIONS;
        expr.optionbits = 1;      // compare for not equal
        if ((expr.reg1 & REG_V) && (dataType & TYP_FLOAT)) {
            expr.optionbits |= 8; // floating point not equal includes unordered
        }
        break;
    case '<' + D2:  // <<
        if (swapped) {expr.etype |= XPR_ERROR; expr.value.u = ERR_WRONG_TYPE;}
        expr.instruction = II_SHIFT_LEFT;  break;
    case '>' + D2:   // >>
        if (swapped) {expr.etype |= XPR_ERROR; expr.value.u = ERR_WRONG_TYPE;}
        expr.instruction = II_SHIFT_RIGHT_S;  break;
    case '>' + D3:   // >>>
        if (swapped) {expr.etype |= XPR_ERROR; expr.value.u = ERR_WRONG_TYPE;}
        expr.instruction = II_SHIFT_RIGHT_U;  break;
    default:
        expr.etype |= XPR_ERROR; expr.value.u = ERR_WRONG_TYPE;
    }
    return expr;
}
 
 
// Interpret dyadic expression generating memory operand. 
// both expressions are inside [] or at least one contains components other than integer constants
SExpression CAssembler::op2Memory(uint32_t op, SExpression & exp1, SExpression & exp2) {
    SExpression expr;                                // return value
    SExpression expt;    // temporary value
    expr.tokens = exp1.tokens + exp2.tokens + 1;  // total number of tokens
    uint64_t f;        // temporary factor
    int32_t symi1 = 0, symi2 = 0;       // symbol indexes
 
    if (!((exp1.etype|exp2.etype) & (XPR_IMMEDIATE|XPR_BASE|XPR_INDEX|XPR_OPTION|XPR_SYM1|XPR_SYM2|XPR_LIMIT|XPR_LENGTH|XPR_BROADC))) {
        // combination of only integer expressions inside []
        // combine everything from the two operands
        expr = exp1 | exp2;
        expr.tokens = exp1.tokens + exp2.tokens + 1;
        expr.etype &= ~XPR_OP;  expr.instruction = 0;  // operator is resolved here
        switch (op) {
        case '+':  // adding offsets
            expr.offset_mem = exp1.offset_mem + exp2.offset_mem;
            break;
        case '-':  // 
            expr.offset_mem = exp1.offset_mem - exp2.offset_mem;
            break;
        case '*':
            expr.offset_mem = exp1.offset_mem * exp2.offset_mem;
            break;
        case '/':
            if (exp2.offset_mem == 0) {
                expr.etype |= XPR_ERROR;
                expr.value.u = ERR_OVERFLOW;
                break;
            }
            expr.offset_mem = exp1.offset_mem / exp2.offset_mem;
            break;
        case '<' + D2:  // <<
            expr.offset_mem = exp1.offset_mem << exp2.offset_mem;
            break;
        case '>' + D2:  // >>  shift right signed
             expr.offset_mem = exp1.offset_mem >> exp2.offset_mem; // signed shift right
            break;
        case '>' + D3:  // >>> unsigned shift right
            expr.offset_mem = uint32_t(exp1.offset_mem) >> uint32_t(exp2.offset_mem); // unsigned shift right
            break;
        default:  // wrong operator
            expr.value.u = ERR_WRONG_TYPE;
            expr.etype |= XPR_ERROR;  return expr;
        }
        return expr;
    }
 
    // not only integer expressions
    if ((exp2.etype & XPR_SYM1) && op == '-') {
        // subtracting two symbol addresses
        if (exp1.sym1) {
            exp2.sym2 = exp2.sym1;  exp2.sym1 = 0;
            exp2.etype = (exp2.etype & ~XPR_SYM1) | XPR_SYM2;
            if (exp1.symscale1 == 0) exp1.symscale1 = 1;
            if (exp2.symscale1 == 0) exp2.symscale1 = 1;
            if (exp1.symscale1 != exp2.symscale1 || exp2.sym2 == 0) {
                exp1.value.u = ERR_CONFLICT_TYPE;  // conflicting scale factors
                exp1.etype |= XPR_ERROR;  return exp1;
            }
        }
        else if (exp1.sym3) { 
            exp2.sym4 = exp2.sym3;  exp2.sym3 = 0;
            exp2.etype = (exp2.etype & ~XPR_SYM1) | XPR_SYM2;
            if (exp1.symscale3 == 0) exp1.symscale3 = 1;
            if (exp2.symscale3 == 0) exp2.symscale3 = 1;
            if (exp1.symscale3 != exp2.symscale3 || exp2.sym4 == 0) {
                exp1.value.u = ERR_CONFLICT_TYPE;  // conflicting scale factors
                exp1.etype |= XPR_ERROR;  return exp1;
            }
        }
        else {
            exp1.value.u = ERR_CONFLICT_TYPE;  // conflicting scale factors
            exp1.etype |= XPR_ERROR;  return exp1;
        }
    }
    // error checks
    if (exp1.etype & exp2.etype & (XPR_SYM1 | XPR_SYM2 | XPR_SYMSCALE | XPR_INDEX 
        | XPR_LIMIT | XPR_LENGTH | XPR_BROADC)) {        
        exp1.value.u = ERR_MEM_COMPONENT_TWICE;              // some component or option specified twice
        exp1.etype |= XPR_ERROR;  return exp1;
    }
    if (((exp1.etype | exp2.etype) & (XPR_LIMIT | XPR_OFFSET)) == (XPR_LIMIT | XPR_OFFSET)) {
        exp1.value.u = ERR_LIMIT_AND_OFFSET;  // cannot have both offset and limit
        exp1.etype |= XPR_ERROR;  return exp1;
    } 
 
    if ((exp2.etype & XPR_BASE) && ((exp1.etype & XPR_BASE) || op == '-')) {
        // adding two registers or subtracting a register. make the second an index register
        if (exp2.base == 31 && (exp1.etype & XPR_BASE) && !(exp2.etype & XPR_INDEX)) {  
            // stack pointer cannot be index. make first register an index instead
            exp1.index = exp1.base; exp1.base = 0;
            exp1.etype = (exp1.etype & ~XPR_BASE) | XPR_INDEX;
            exp1.scale = 1;
        }
        else {
            exp2.index = exp2.base; exp2.base = 0;
            exp2.etype = (exp2.etype & ~XPR_BASE) | XPR_INDEX;
            exp2.scale = 1;
        }
    }
    // combine everything from the two operands
    expr = exp1 | exp2;
    expr.tokens = exp1.tokens + exp2.tokens + 1;
    expr.value.u = exp1.value.u + exp2.value.u;  // add values, except for special cases below
    expr.offset_mem = exp1.offset_mem + exp2.offset_mem;        // add offsets, except for special cases below
    expr.offset_jump = exp1.offset_jump + exp2.offset_jump;     // add jump offsets
    expr.etype &= ~XPR_OP;  expr.instruction = 0;  // operator is resolved here
 
    switch (op) {
    case '+':  // adding components. offsets have been added above
        /* Changed: immediate value outside [] cannot be converted to offset:
        if ((expr.etype & (XPR_REG | XPR_BASE | XPR_SYM1)) && (expr.etype & XPR_INT) && (expr.etype & XPR_MEM)) {
            // adding offset. convert value to offset
            expr.offset += expr.value.i;
            expr.value.i = 0;
            expr.etype = (expr.etype | XPR_OFFSET) & ~XPR_IMMEDIATE;
        } */
        break;
    case ',':  // combining components. components are combined below
        if (exp1.value.u && exp2.value.u) {          
            expr.value.u = ERR_WRONG_TYPE;       // cannot combine integer offsets with comma operator
            expr.etype |= XPR_ERROR;  return expr;
        }
        if ((expr.etype & XPR_INDEX) && (expr.etype & (XPR_LENGTH | XPR_BROADC))) { // both index and broadcast
            if (expr.scale == -1) {
                if (expr.index != expr.length) { // scale = -1. index and length must be the same
                    expr.value.u = ERR_NEG_INDEX_LENGTH;
                    expr.etype |= XPR_ERROR;  return expr;
                }
            }
            else { // cannot have index and length/broadcast
                expr.value.u = ERR_INDEX_AND_LENGTH;
                expr.etype |= XPR_ERROR;  return expr;
            }
        }
        break;
    case '-':  // subtract offsets or registers (symbol addresses subtracted above)
        /* Changed: immediate value outside [] cannot be converted to offset:
        if ((exp1.etype & (XPR_REG | XPR_BASE | XPR_SYM1)) && (exp2.etype & XPR_INT) && (expr.etype & XPR_MEM)) {
            // subtracting offset. convert value to offset
            expr.offset = exp1.offset - exp2.value.i;
            expr.value.i = 0;
            expr.etype = (expr.etype | XPR_OFFSET) & ~XPR_IMMEDIATE;
        }
        else  */
        {
        expr.offset_mem = exp1.offset_mem - exp2.offset_mem;
        expr.offset_jump = exp1.offset_jump - exp2.offset_jump;
        expr.value.u = exp1.value.u - exp2.value.u; 
        }
        if (exp2.etype & XPR_INDEX) {  // subtracting a register gives negative index
            expr.scale = - exp2.scale;
        }
        else if ((exp1.etype & XPR_SYM1) && (exp2.etype & XPR_SYM2)) {
            // subtracting two symbols. has been fixed above
            // check if symbols are in the same domain
            if (exp1.sym1) {
                symi1 = findSymbol(exp1.sym1);
                symi2 = findSymbol(exp2.sym2);
            }
            else if (exp1.sym3) {
                symi1 = findSymbol(exp1.sym3);
                symi2 = findSymbol(exp2.sym4);
            }
            if (symi1 > 0 && symi2 > 0 
                && (symbols[symi1].st_other & symbols[symi2].st_other & (SHF_IP | SHF_DATAP | SHF_THREADP)) == 0
                && (symbols[symi1].st_type & symbols[symi2].st_type & STT_CONSTANT) == 0) {
                errors.reportLine(ERR_RELOCATION_DOMAIN);
            }
        }        
        //else if ((expr.etype & XPR_IMMEDIATE) == XPR_INT) expr.etype |= XPR_OFFSET;  // value is offset
        if (exp2.etype & (XPR_SYM1|XPR_SYMSCALE)) {
            expr.value.u = ERR_WRONG_TYPE;    // cannot subtract these components
            expr.etype |= XPR_ERROR;  return expr;
        }
        break;
    case '<'+D2:  // index << s = index * (1 << s)
        //exp2.value.u = (uint64_t)1 << exp2.value.u; 
        exp2.offset_mem = 1 << exp2.offset_mem;
        goto MULTIPLYINDEX; // continue in case '*'
    case '*':  // indexregister * scale
        if ((exp1.etype & (XPR_INT|XPR_OFFSET)) && (exp2.etype & (XPR_BASE|XPR_INDEX))){
            // first operand is integer, second operand is register. swap operands
            expt = exp2;  exp2 = exp1;  exp1 = expt;
        }
        MULTIPLYINDEX:
        if ((exp1.etype & XPR_BASE) && !(exp1.etype & XPR_INDEX)) {  // convert base to index
            exp1.index = exp1.base;  exp1.base = 0;  exp1.scale = 1;
            exp1.etype = (exp1.etype & ~XPR_BASE) | XPR_INDEX;
        }
        if (exp2.etype & XPR_INT) {  // convert integer to offset. should not occur
            exp2.offset_mem = exp2.value.w; exp2.value.i = 0;
            exp2.etype = (exp2.etype & ~XPR_INT) | XPR_OFFSET;
        }
        if (!(exp1.etype & XPR_INDEX) || !(exp2.etype & XPR_OFFSET)
            || ((exp1.etype | exp2.etype) & (XPR_OPTION|XPR_SYM1|XPR_SYM2|XPR_LIMIT|XPR_LENGTH|XPR_BROADC))) {
            expr.value.u = ERR_WRONG_TYPE;    // cannot multiply anything else
            expr.etype |= XPR_ERROR;  return expr;
        }
        f = int64_t(exp2.offset_mem) * exp1.scale;
        if ((f & (f - 1)) || f == 0 || f > 16) {  // check that scale is a power of 2, not bigger than 16
            expr.value.u = ERR_SCALE_FACTOR;    // wrong scale factor
            expr.etype |= XPR_ERROR;  return expr;
        }
        expr.base = exp1.base;  expr.index = exp1.index;
        expr.scale = (int8_t)f;
        expr.etype = exp1.etype | (exp2.etype & ~(XPR_INT|XPR_OFFSET));
        expr.value.u = 0;
        expr.offset_mem = exp1.offset_mem;
        break;
    case '>'+D2:  // divide (sym1-sym2) >> s = (sym1-sym2) / (1 << s)
        exp2.value.u = (uint64_t)1 << exp2.value.u;
        exp2.offset_mem = (uint64_t)1 << exp2.offset_mem;
        // continue in case '/'
    case '/':  // divide (sym1-sym2) / scale
        if ((exp2.etype & XPR_OFFSET) && !(exp2.etype & (XPR_REG | XPR_INT | XPR_BASE))) {
            // constant has been interpreted as offset because it is inside []. change it to XPR_INT
            exp2.value.i = exp2.offset_mem; exp2.offset_mem = 0;
            exp2.etype = (exp2.etype & ~(XPR_OFFSET)) | XPR_INT;        
            expr.offset_mem = exp1.offset_mem;
        } 
        if (!(exp1.etype & XPR_SYM1) || ((exp2.etype & 0xF) != XPR_INT)
            || ((exp1.etype | exp2.etype) & (XPR_REG|XPR_OPTION|XPR_LIMIT|XPR_LENGTH|XPR_BROADC))) {
            expr.value.u = ERR_WRONG_TYPE;    // cannot divide anything else
            expr.etype |= XPR_ERROR;  return expr;
        }
        f = exp2.value.u;
        if (exp1.symscale1) f *= exp1.symscale1;
        if ((f & (f - 1)) || f == 0 || f > 16) {  // check that scale is a power of 2, not bigger than 16
            expr.value.u = ERR_SCALE_FACTOR;    // wrong scale factor
            expr.etype |= XPR_ERROR;  return expr;
        }
        expr.symscale1 = (int8_t)f;
        expr.etype |= XPR_SYMSCALE;
        expr.etype = exp1.etype | (exp2.etype & ~XPR_INT);
        expr.value.u = exp1.value.u;
        break;
    case '=':  // option = value
        // check if operands contain anything else
        if (!(exp1.etype & XPR_OPTION) || !(exp2.etype & (XPR_INT | XPR_BASE | XPR_REG))
            || ((exp1.etype | exp2.etype) & (XPR_SYM1|XPR_SYM2|XPR_REG2|XPR_INDEX|XPR_LIMIT|XPR_LENGTH|XPR_BROADC))) {
            expr.value.u = ERR_WRONG_TYPE;    // cannot uses '=' on anyting else inside []
            expr.etype |= XPR_ERROR;  return expr;
        }
        switch (exp1.value.w) {
        case OPT_LENGTH:  // length = register
            if ((exp2.etype & XPR_REG1) && (exp2.reg1 & REG_R)) {
                // length = register, outside []
                expr.etype = XPR_LENGTH | XPR_MEM;
                expr.length = exp2.reg1;
                expr.base = 0;
                expr.value.i = 0;
                break;
            }
            // length = register, inside []
            if (!(exp2.etype & XPR_BASE) || (exp2.base & 0xE0) != REG_R) {
                expr.value.u = ERR_WRONG_TYPE;    // cannot uses '=' on anyting else inside []
                expr.etype |= XPR_ERROR;  return expr;
            }
            expr.etype = XPR_LENGTH | XPR_MEM;
            expr.length = exp2.base;
            expr.base = 0;
            expr.value.i = 0;
            break;
        case OPT_BROADCAST:  // broadcast = register
            if (!(exp2.etype & XPR_BASE) || (exp2.base & 0xE0) != REG_R) {
                expr.value.u = ERR_WRONG_TYPE;    // cannot uses '=' on anyting else inside []
                expr.etype |= XPR_ERROR;  return expr;
            }
            expr.etype = XPR_BROADC | XPR_MEM;
            expr.length = exp2.base;
            expr.base = 0;
            expr.value.i = 0;
            break;
        case OPT_LIMIT:   // limit = integer
            if (!(exp2.etype & XPR_INT)) {
                expr.value.u = ERR_WRONG_TYPE;    // cannot uses '=' on anyting else inside []
                expr.etype |= XPR_ERROR;  return expr;
            }
            if (exp1.etype & XPR_OFFSET) {  // cannot have both limit and offset
                expr.etype = ERR_LIMIT_AND_OFFSET;
                expr.etype |= XPR_ERROR;  return expr;
            }
            expr.etype = XPR_LIMIT | XPR_MEM;
            expr.value.u = exp2.value.u;
            break;
        case OPT_SCALAR:  // scalar
            expr.etype = XPR_SCALAR | XPR_MEM;
            expr.value.i = 0;
            break;
        case OPT_MASK:
            if (!(exp2.etype & (XPR_REG | XPR_REG1))) {
                expr.etype = ERR_MASK_NOT_REGISTER;
                expr.etype |= XPR_ERROR;  return expr;
            }
            expr.etype = XPR_MASK;
            expr.mask = exp2.reg1;
            expr.reg1 = 0;
            break;
        case OPT_FALLBACK:
            if (exp2.etype == (XPR_REG | XPR_REG1) && (exp2.reg1 & 0x1F) != 0x1F) {
                expr.fallback = exp2.reg1;
                expr.etype = XPR_FALLBACK;
                expr.reg1 = 0;
            }
            else if ((exp2.etype & XPR_IMMEDIATE) && exp2.value.i == 0){
                expr.fallback = (expr.mask & 0xF0) | 0x1F;            
                expr.etype = XPR_FALLBACK;
            }
            else {
                expr.value.u = ERR_FALLBACK_WRONG;
                expr.etype |= XPR_ERROR;  return expr;
            }
            break;
        case OPT_OPTIONS:
            if ((exp2.etype & 0xF) == XPR_INT) {
                expr.etype = (expr.etype & ~XPR_IMMEDIATE) | XPR_OPTIONS;
                expr.optionbits = (uint8_t)exp2.value.u;  // move value to optionbits
                expr.value.i = 0;
                return expr;
            }
            else {
                expr.etype = ERR_WRONG_TYPE;
                expr.etype |= XPR_ERROR;  
                return expr;
            }
            break;
        default:  // mask and fallback options not allowed inside []
            expr.value.u = ERR_NOT_INSIDE_MEM;  // change error message
            expr.etype |= XPR_ERROR;  return expr;
        }
        break;
 
    default:  // wrong operator
        expr.value.u = ERR_WRONG_TYPE;
        expr.etype |= XPR_ERROR;  return expr;
    }
    if ((expr.etype & XPR_INT) && !(expr.etype & (XPR_SYM1 | XPR_INDEX))) {           // value not used otherwise is offset
        expr.etype = (expr.etype & ~(XPR_INT)) | XPR_OFFSET;
    }
    return expr;
}
 
 
// Interpreted triadic expression exp1 ? exp2 : exp3 at the indicated positions
SExpression CAssembler::op3(uint32_t tok1, uint32_t toklow, uint32_t tokcolon, uint32_t maxtok, uint32_t options) {
    SExpression exp1, exp2;
    uint32_t cond;   // evaluated condition
 
    exp1 = expression(tok1, toklow - tok1, options);  // evaluate expression before '?'
    if (exp1.tokens != toklow - tok1) errors.report(tokens[tok1 + exp1.tokens]);
 
    if ((exp1.etype & XPR_REG) == 0 && (exp1.etype & (XPR_INT | XPR_FLT | XPR_STRING))) {
        // condition is a constant. just choose one of the two operands
 
        if ((exp1.etype & 0xF) == XPR_FLT) cond = exp1.value.d != 0.; // evaluate condition to true or false
        else if ((exp1.etype & 0xF) == XPR_STRING) {   // string is false if empty or "0"
            cond = (exp1.sym2 != 0 && (exp1.sym2 > 1 || stringBuffer.get<uint16_t>((uint32_t)exp1.value.u) != '0'));
        }
        else cond = exp1.value.i != 0;
 
        // the expression that is not selected is evaluated with option = 0x10 to suppress errors but still count the tokens
        exp1 = expression(toklow + 1, tokcolon - (toklow + 1), options | (cond ^ 1) << 4);   // evaluate first expression
        if (exp1.tokens != tokcolon - (toklow + 1)) errors.report(tokens[toklow + 1 + exp1.tokens]);
        exp2 = expression(tokcolon + 1, tok1 + maxtok - (tokcolon + 1), options | cond << 4);      // evaluate second expression
 
        // number of tokens
        exp1.tokens = exp2.tokens = tokcolon - tok1 + 1 + exp2.tokens;
 
        // return the chosen expression
        if (cond) return exp1; else return exp2;
    }
 
    // condition is not a constant. It must be a mask register
    if ((exp1.etype & XPR_REG) == 0 || exp1.reg1 == 0 || exp1.etype & (XPR_OP|XPR_OPTION|XPR_MEM|XPR_SYM1|XPR_MASK|XPR_UNRESOLV)) {
        errors.report(tokens[tok1].pos, tokens[tok1].stringLength, ERR_MASK_NOT_REGISTER);
    }
    uint8_t maskreg = exp1.reg1;  // save mask register
 
    // evaluate the middle expression
    exp1 = expression(toklow + 1, tokcolon - (toklow + 1), options);
    if (exp1.tokens != tokcolon - (toklow + 1)) errors.report(tokens[toklow + 1 + exp1.tokens]);
 
    // third expression must be fallback
    exp2 = expression(tokcolon + 1, tok1 + maxtok - (tokcolon + 1), options);
    uint8_t fallbackreg = 0;  // fallback register
    if (exp2.etype & XPR_REG) {
        fallbackreg = exp2.reg1;
        exp1.etype |= XPR_FALLBACK;
    }
    else if ((exp2.etype & (XPR_INT | XPR_FLT)) && exp2.value.i == 0) {
        fallbackreg = maskreg | 0x1F;  // register 31 with same type as mask register    
        exp1.etype |= XPR_FALLBACK;
    }
    if (exp2.etype & (XPR_STRING | XPR_OP | XPR_OPTION | XPR_MEM | XPR_SYM1 | XPR_MASK) || exp2.value.i) {
        errors.report(tokens[tokcolon+1].pos, tokens[tokcolon+exp2.tokens+1].pos  - tokens[tokcolon+1].pos, ERR_FALLBACK_WRONG);
    }
    // insert mask and fallback in exp1
    exp1.etype |= XPR_MASK;
    exp1.mask = maskreg;
    exp1.fallback = fallbackreg;
    exp1.tokens = tokcolon - tok1 + 1 + exp2.tokens;
    return exp1;
}
 
 
// Convert -(expression), e.g. -(A-B)
SExpression CAssembler::op1minus(SExpression & exp1) {
    exp1.tokens++;
    if ((exp1.etype & (XPR_REG | XPR_MEM)) && !(exp1.etype & XPR_OP) && exp1.value.i == 0) {  // -reg or -mem
        exp1.etype |= XPR_OP | XPR_INT;
        exp1.instruction = II_SUB_REV;              // 0 - expression
    }
    else if (exp1.instruction == II_SUB) exp1.instruction = II_SUB_REV;
    else if (exp1.instruction == II_SUB_REV) exp1.instruction = II_SUB;
    else if (exp1.instruction == II_ADD_ADD) exp1.optionbits ^= 3;
    else if (exp1.instruction == II_MUL_ADD || exp1.instruction == II_MUL_ADD2) exp1.optionbits ^= 0xF;
    else if (exp1.instruction == II_ADD && !(exp1.etype & (XPR_IMMEDIATE | XPR_MEM | XPR_SYM1))) {
        // -(R1+R2) = -R1 -R2 + 0
        exp1.instruction = II_ADD_ADD;
        exp1.value.i = 0;
        exp1.optionbits = 3;
        exp1.etype |= XPR_INT;
    }
    else if (exp1.instruction == II_ADD && (exp1.etype & XPR_IMMEDIATE)) {
        // -(R1+I) = -R1 + (-I)
        exp1.instruction = II_SUB_REV;
        if ((exp1.etype & XPR_IMMEDIATE) == XPR_FLT) exp1.value.d = -exp1.value.d;
        else exp1.value.i = -exp1.value.i;
    }
    else if ((exp1.instruction == 0 || exp1.instruction == II_MUL || exp1.instruction == II_DIV || exp1.instruction == II_DIV_REV)
        && (exp1.etype & XPR_IMMEDIATE)) {
        // -I or -(A*I)
        if (exp1.etype & XPR_FLT) exp1.value.d = -exp1.value.d;
        else exp1.value.i = -exp1.value.i;
    }
    else if (exp1.instruction == II_MUL && !(exp1.etype & XPR_IMMEDIATE)) {
        exp1.instruction = II_MUL_ADD;
        exp1.optionbits ^= 3;
        exp1.etype |= XPR_INT;
    }
    else {
        exp1.etype = XPR_ERROR;
        exp1.value.u = ERR_TOO_COMPLEX; // cannot apply '-' to other expressions
    }
    return exp1;
}
 
// Interpret dyadic expression with string operands
SExpression CAssembler::op2String(uint32_t op, SExpression const & exp1, SExpression const & exp2) {
    if (op != '+') {
        SExpression exp3;
        exp3.etype = XPR_ERROR;
        exp3.value.u = ERR_WRONG_TYPE;
        return exp3;
    }
    // operation is +. concatenate strings, convert numeric to string
 
    uint32_t stringpos1 = stringBuffer.dataSize();  // current position in string buffer
    uint32_t stringpos2;                            // position of second part of concatenated string
    const int maxIntLength = 32;                    // maximum length of integer as string
    const int maxFloatLength = 48;
    const char * wrongType = "-wrong type!-";
    uint32_t len = 0;                               // length of string
 
    // first operand
    if (exp1.etype == XPR_STRING) {
        stringBuffer.push(stringBuffer.buf() + exp1.value.u, exp1.sym2); // copy to string buffer without terminating zero
        stringBuffer.put((char)0);
        //stringpos2 = stringBuffer.dataSize();
    }
    else if (exp1.etype == XPR_INT) {  // convert integer to string
        stringBuffer.push(&exp1, maxIntLength);  // put in anyting here to make space for writing string
        if (sizeof(long int) >= 8) {        
#ifndef _WIN32  // suppress warning
            sprintf((char*)stringBuffer.buf()+stringpos1, "%li", exp1.value.i);
#endif
        }
        else {
            sprintf((char*)stringBuffer.buf()+stringpos1, "%lli", (long long)exp1.value.i);
        }
    }
    else if (exp1.etype == XPR_FLT) {  // convert float to string
        stringBuffer.push(&exp1, maxFloatLength);  // put in anyting here to make space for writing string
        sprintf((char*)stringBuffer.buf()+stringpos1, "%g", exp1.value.d);
    }
    else {
        stringBuffer.put(wrongType);
    }
    len = (uint32_t)strlen((char*)stringBuffer.buf()+stringpos1);
    stringpos2 = stringpos1 + len;
    stringBuffer.setSize(stringpos2);  // remove extra space
 
    // second operand
    if (exp2.etype == XPR_STRING) {
        stringBuffer.push(stringBuffer.buf() + exp2.value.u, exp2.sym2); // copy to string buffer without terminating zero
        stringBuffer.put((char)0);
    }
    else if (exp2.etype == XPR_INT) {  // convert integer to string
        stringBuffer.push(&exp2, maxIntLength);  // put in anyting here to make space for writing string
        if (sizeof(long int) >= 8) {
#ifndef _WIN32  // suppress warning
            sprintf((char*)stringBuffer.buf()+stringpos2, "%li", exp2.value.i);
#endif
        }
        else {
            sprintf((char*)stringBuffer.buf()+stringpos2, "%lli", (long long)exp2.value.i);
        }
        len = (uint32_t)strlen((char*)stringBuffer.buf()+stringpos2);
        stringBuffer.setSize(stringpos2 + len + 1);
    }
    else if (exp2.etype == XPR_FLT) {  // convert float to string
        stringBuffer.push(&exp2, maxFloatLength);  // put in anyting here to make space for writing string
        sprintf((char*)stringBuffer.buf()+stringpos2, "%g", exp2.value.d);
        len = (uint32_t)strlen((char*)stringBuffer.buf()+stringpos2);
        stringBuffer.setSize(stringpos2 + len + 1);
    }
    else {
        stringBuffer.put(wrongType);
    }
    SExpression exp3;
    exp3.etype = XPR_STRING;
    exp3.value.u = stringpos1;
    exp3.sym2 = (uint32_t)strlen((char*)stringBuffer.buf() + stringpos1);
    exp3.tokens = exp1.tokens + exp2.tokens + 1;
    return exp3;
}
 
 
double interpretFloat(const char * s, uint32_t length) {
    // interpret floating point number from string with indicated length
    char buffer[64];
    if (length >= sizeof(buffer)) {
        union {
            uint64_t i;
            double d;
        } nan = {0xFFFFC00000000000};
        return nan.d; // return NAN
    }
    memcpy(buffer, s, length);
    buffer[length] = 0;          // terminate string
    double r;
    sscanf(buffer, "%lf", &r);   // convert string to double
    return r;
} 
 
// make expression out of symbol
SExpression CAssembler::symbol2expression(uint32_t symi) {
    SExpression expr;
    zeroAllMembers(expr);
 
    switch (symbols[symi].st_type) {
    case STT_CONSTANT:  case STT_VARIABLE:
        expr.etype = XPR_INT;   // default type
        expr.sym1 = symi;
        if (symbols[symi].st_other & STV_FLOAT) expr.etype = XPR_FLT;
        if (symbols[symi].st_other & STV_STRING) {
            expr.etype = XPR_STRING;
            expr.sym2 = (uint32_t)symbols[symi].st_unitnum;
        }
        expr.value.u = symbols[symi].st_value;
        break;
    case STT_EXPRESSION:
        if (symbols[symi].st_value < expressions.numEntries()) {
            expr = expressions[uint32_t(symbols[symi].st_value)];
        }
        else {
            expr.etype = XPR_ERROR;
            expr.value.u = TOK_XPR;
        }
        break;
    default:
        expr.etype = XPR_ERROR; 
        expr.value.u = ERR_CONFLICT_TYPE;
    }
    expr.tokens = 0;
    return expr;
}
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.