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

Subversion Repositories forwardcom

[/] [forwardcom/] [bintools/] [assem1.cpp] - Rev 64

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

/****************************    assem1.cpp    ********************************
* Author:        Agner Fog
* Date created:  2017-04-17
* Last modified: 2021-07-10
* Version:       1.11
* Project:       Binary tools for ForwardCom instruction set
* Module:        assem.cpp
* Description:
* Module for assembling ForwardCom .as files. Contains:
* pass1(): Split input file into lines and tokens. Remove comments. Find symbol definitions
* pass2(): Handle meta code. Classify lines. Identify symbol names, sections, functions
*
* Copyright 2017-2021 GNU General Public License http://www.gnu.org/licenses
******************************************************************************/
#include "stdafx.h"
 
const char * allowedInNames = "_$@";   // characters allowed in symbol names (don't allow characters that are used as operators)
const bool allowUTF8 = true;           // UTF-8 characters allowed in symbol names
const bool allowNestedComments = true; // allow nested comments: /* /* */ */
 
                                       // Operator for sorting symbols by name. Used by assembler
// List of operators
SOperator operatorsList[] = {
    // name, id, priority
    {"(", '(',      1},
    {")", ')',      1},
    {"[", '[',      1},
    {"]", ']',      1},
    {"{", '{',      1},
    {"}", '}',      1},
    {"'", 39,       1},
    {"\"", '"',     1},           // "
    {"/*", 'c',     1},           // comment begin
    {"*/", 'd',     1},           // comment end
    {".", '.',      2},
    {"!", '!',      3},
    {"~", '~',      3},
    {"++", '+'+D2,  3},
    {"--", '-'+D2,  3},
    {"*", '*',      4},
    {"/", '/',      4},
    {"%", '%',      4},
    {"+", '+',      5},
    {"-", '-',      5},
    {"<<", '<'+D2,  6},
    {">>", '>'+D2,  6},           // signed shift right
    {">>>", '>'+D3, 6},           // unsigned shift right
    {"<", '<',      7},
    {"<=", '<'+EQ,  7},
    {">", '>',      7},
    {">=", '>'+EQ,  7},
    {"==", '='+D2,  8},
    {"!=", '!'+EQ,  8},
    {"&", '&',      9},
    {"^", '^',     10},
    {"|", '|',     11},
    {"&&", '&'+D2, 12},
    {"||", '|'+D2, 13},
    {"^^", '^'+D2, 13},           // boolean XOR. non-standard operator
    {"?", '?',     14},
    {":", ':',     14},
    {"=", '=',     15},
    {"+=", '+'+EQ, 15},
    {"-=", '-'+EQ, 15},
    {"*=", '*'+EQ, 15},
    {"/=", '/'+EQ, 15},
    {"%=", '%'+EQ, 15},
    {"<<=", '<'+D2+EQ,  15},
    {">>=", '>'+D2+EQ,  15},      // signed shift right
    {">>>=", '>'+D3+EQ, 15},      // unsigned shift right
    {"&=", '&'+EQ, 15},
    {"^=", '^'+EQ, 15},
    {"|=", '|'+EQ, 15},
    {",", ',',     16},
    {"//", '/'+D2, 20},           // comment, end of line
    {";", ';',     20}            // comment, end of line
};
 
 
// List of keywords
SKeyword keywordsList[] = {
    // name, id
    {"section",        DIR_SECTION},        // TOK_DIR: section, functions directives
    {"function",       DIR_FUNCTION},
    {"end",            DIR_END},
    {"public",         DIR_PUBLIC},
    {"extern",         DIR_EXTERN},
 
    // TOK_ATT: attributes of sections, functions and symbols
    {"read",           ATT_READ},           // readable section
    {"write",          ATT_WRITE},          // writeable section
    {"execute",        ATT_EXEC},           // executable section
    {"align",          ATT_ALIGN},          // align section, data, or code
    {"weak",           ATT_WEAK},           // weak linking
    {"reguse",         ATT_REGUSE},         // register use    
    {"constant",       ATT_CONSTANT},       // external constant
    {"uninitialized",  ATT_UNINIT},         // uninitialized section (BSS)
    {"communal",       ATT_COMDAT},         // communal section. duplicates and unreferenced sections are removed
    {"exception_hand", ATT_EXCEPTION},      // exception handler and stack unroll information
    {"event_hand",     ATT_EVENT},          // event handler list, including constructors and destructors
    {"debug_info",     ATT_DEBUG},          // debug information
    {"comment_info",   ATT_COMMENT},        // comments, including copyright and required libraries
 
    // TOK_TYP: type names
    {"int8",           TYP_INT8},      
    {"uint8",          TYP_INT8+TYP_UNS},
    {"int16",          TYP_INT16},
    {"uint16",         TYP_INT16+TYP_UNS},
    {"int32",          TYP_INT32},
    {"uint32",         TYP_INT32+TYP_UNS},
    {"int64",          TYP_INT64},
    {"uint64",         TYP_INT64+TYP_UNS},
    {"int128",         TYP_INT128},
    {"uint128",        TYP_INT128+TYP_UNS},
    {"int",            TYP_INT32},
    {"uint",           TYP_INT32+TYP_UNS},
    {"float",          TYP_FLOAT32},
    {"double",         TYP_FLOAT64},
    {"float16",        TYP_FLOAT16},
    {"float32",        TYP_FLOAT32},
    {"float64",        TYP_FLOAT64},
    {"float128",       TYP_FLOAT128},
    {"string",         TYP_STRING},  
 
    // TOK_OPT: options of instructions and operands
    {"mask",           OPT_MASK},      
    {"fallback",       OPT_FALLBACK},
    {"length",         OPT_LENGTH},
    {"broadcast",      OPT_BROADCAST},    
    {"limit",          OPT_LIMIT},
    {"scalar",         OPT_SCALAR},
    {"options",        OPT_OPTIONS}, 
    {"option",         OPT_OPTIONS},  // alias
 
    // TOK_REG: register names    
    {"numcontr",       REG_NUMCONTR},      
    {"threadp",        REG_THREADP},      
    {"datap",          REG_DATAP},
    {"ip",             REG_IP},
    {"sp",             REG_SP},
 
    // TOK_HLL: high level language keywords
    {"if",             HLL_IF},         
    {"else",           HLL_ELSE},
    {"switch",         HLL_SWITCH},         // switch (r1, scratch registers) { case 0: break; ...}
    {"case",           HLL_CASE},
    {"for",            HLL_FOR},            // for (r1 = 1; r1 <= r2; r1++) {}
    {"in",             HLL_IN},             // for (float v1 in [r1-r2], nocheck) // (r2 counts down)
    {"while",          HLL_WHILE},          // while (r1 > 0) {}
    {"do",             HLL_DO},             // do {} while ()
    {"break",          HLL_BREAK},          // break out of switch or loop
    {"continue",       HLL_CONTINUE},       // continue loop 
    {"true",           HLL_TRUE},           // constant = 1
    {"false",          HLL_FALSE},          // constant = 0
 
    // temporary additions. will be replaced by macros later:
    {"push",           HLL_PUSH},           // push registers
    {"pop",            HLL_POP},            // pop registers
 
};
 
// List of register name prefixes
SKeyword registerNames[] = {
    // name, id
    {"r",            REG_R},
    {"v",            REG_V},
    {"spec",         REG_SPEC},
    {"capab",        REG_CAPAB},
    {"perf",         REG_PERF},
    {"sys",          REG_SYS}
};
 
 
CAssembler::CAssembler() {                                 // Constructor
    // Reserve size for buffers
    const int estimatedLineLength = 16;
    const int estimatedTokensPerLine = 10;
    int estimatedNumLines = dataSize() / estimatedLineLength;
    lines.setNum(estimatedNumLines);
    tokens.setNum(estimatedNumLines * estimatedTokensPerLine);
    errors.setOwner(this);
    // Initialize and sort lists
    initializeWordLists();
    ElfFwcShdr nullHeader;         // make first section header empty
    zeroAllMembers(nullHeader);
    sectionHeaders.push(nullHeader);
}
 
void CAssembler::go() {
 
    // Write feedback text to console
    feedBackText1();
 
    // Set default options
    if (cmd.codeSizeOption == 0) cmd.codeSizeOption = 1 << 24;
    if (cmd.dataSizeOption == 0) cmd.dataSizeOption = 1 << 15;
    // initialize options
    code_size = cmd.codeSizeOption;
    data_size = cmd.dataSizeOption;
 
    do {  // This loop is repeated only once. Just convenient to break out of in case of errors
        pass = 1;
        // Split input file into lines and tokens. Find symbol definitions
        pass1();
        if (errors.tooMany()) {err.submit(ERR_TOO_MANY_ERRORS);  break;}
 
        pass = 2;
        // A. Handle metaprogramming directives
        // B. Classify lines
        // C. Identify symbol names, sections, labels, functions 
        pass2();
        if (errors.tooMany()) {err.submit(ERR_TOO_MANY_ERRORS);  break;}
 
        //showTokens();  //!! for debugging only
        //showSymbols(); //!! for debugging only        
 
        pass = 3;
        // Interpret lines. Generate code and data
        pass3();
        if (errors.tooMany()) {err.submit(ERR_TOO_MANY_ERRORS);  break;}
 
        pass = 4;
        // Resolve internal cross references, optimize forward references
        pass4();
        if (errors.tooMany()) {err.submit(ERR_TOO_MANY_ERRORS);  break;}
 
        pass = 5;
        // Make binary file
        pass5();
        if (errors.tooMany()) {err.submit(ERR_TOO_MANY_ERRORS);  break;}
 
    } while (false);
 
    // output any error messages
    errors.outputErrors();
    if (errors.numErrors()) cmd.mainReturnValue = 1; // make sure makefile process stops on error
 
    // output object file
    outFile.write(cmd.getFilename(cmd.outputFile));
}
 
 
// Character can be the start of a symbol name
inline bool nameChar1(char c) {
    return ((c | 0x20) >= 'a' && (c | 0x20) <= 'z') || ((c & 0x80) && allowUTF8) || strchr(allowedInNames, c);
}
 
// Character can be the part of a symbol name
inline bool nameChar2(char c) {
    return nameChar1(c) || (c >= '0' && c <= '9');
}
 
// check if string is a number. Can be decimal, binary, octal, hexadecimal, or floating point
// Returns the length of the part of the string that belongs to the number
uint32_t isNumber(const char * s, int maxlen, bool * isFloat) {
    bool is_float = false;
    char c = s[0];
    if ((c < '0' || c > '9') && (c != '.' || s[1] < '0' || s[1] > '9')) return 0;
    int i = 0;
    int state = 0;  
    // 0: begin
    // 1: after 0
    // 2: after digits 0-9
    // 3: after 0x
    // 4: after 0b or 0o
    // 5: after .
    // 6: after E
    // 7: after E09
    // 8: after E+-
    for (i = 0; i < maxlen; i++) {
        c = s[i];
        char cl = c | 0x20;   // upper case letter
        if (c == '0' && state == 0) {state = 1; continue;}
        if (cl == 'x' && state == 1) {state = 3; continue;}
        if ((cl == 'b' || cl == 'o') && state == 1) {state = 4; continue;}
        if (c == '.' && state <= 2)  {state = 5; is_float = true; continue;}
        if (cl == 'e' && (state <= 2 || state == 5)) {state = 6; is_float = true; continue;}
        if ((c == '+' || c == '-') && state == 6) {state = 8; continue;}
        if (c >= '0' && c <= '9') {
            if (state < 2) state = 2;
            if (state == 6) state = 7;
            continue;
        }
        if (cl >= 'a' && cl <= 'f' && state == 3) continue;
        // Anything else: stop here
        break;
    }
    if (isFloat) *isFloat = is_float;       // return isFloat
    return i;                               // return length
}
 
// Check if string is a register name
uint32_t isRegister(const char * s, uint32_t len) {
    uint32_t i, j, nl, num;
    for (i = 0; i < TableSize(registerNames); i++) {
        if ((s[0] | 0x20) == registerNames[i].name[0]) {   // first character match, lower case
            nl = (uint32_t)strlen(registerNames[i].name);  // length of register name prefix
            if (len < nl + 1 || len > nl + 2) continue;    // continue search if length wrong
            for (j = 0; j < nl; j++) {                     // check if each character matches
                if ((s[j] | 0x20) != registerNames[i].name[j]) { // lower case compare
                    j = 0xFFFFFFFF; break;
                }
            }
            if (j == 0xFFFFFFFF) continue;                 // no match
            if (s[j] < '0' || s[j] > '9') continue;        // not a number
            num = s[j] - '0';                              // get number, first digit
            if (len == nl + 2) {                           // two digit number
                if (s[j+1] < '0' || s[j+1] > '9') continue;// second digit not a number            
                num = num * 10 + (s[j+1] - '0');
            }
            if (num >= 32) continue;                       // number too high
            return num + registerNames[i].id;              // everyting matches
        }
    }
    return 0;       // not found. return 0
} 
 
// write feedback text on stdout
void CAssembler::feedBackText1() {
    if (cmd.verbose) {
        // Tell what we are doing:
        printf("\nAssembling %s to %s", cmd.getFilename(cmd.inputFile), cmd.getFilename(cmd.outputFile));
    }
}
 
 
// Split input file into lines and tokens. Handle preprocessing directives. Find symbol definitions
void CAssembler::pass1() {
    uint32_t n = 0;                // offset into assembly file
    uint32_t m;                    // end of current token
    int32_t  i, f;                 // temporary
    int32_t  comment = 0;          // 0: normal, 1: inside comment to end of line, 2: inside /* */ comment
    uint32_t commentStart = 0;     // start position of multiline comment
    uint32_t commentStartColumn = 0;// start column of multiline comment
    char c;                        // current character or byte
    SToken token = {0};            // current token
    SKeyword keywSearch;           // record to search for keyword
    SOperator opSearch;            // record to search for operator
    SInstruction instructSearch;   // record to search for instruction
    SLine line = {0,0,0,0,0,0,0};  // line record
    lines.push(line);              // empty records for line 0
    linei = 1;                     // start at line 1
    numSwitch = 0;              // count switch statements
    tokens.push(token);            // unused token 0
 
    if (dataSize() >= 3 && (get<uint32_t>(0) & 0xFFFFFF) == 0xBFBBEF) {        
        n += 3;                    // skip UTF-8 byte order mark
    }
 
    line.beginPos = n;             // start of line 1
    line.firstToken = tokens.numEntries();
    line.file = filei;
 
    // loop through file
    while (n < dataSize()) {
        c = get<char>(n);              // get character
 
        // is it space or a control character?
        if (uint8_t(c) <= 0x20) {                
            if (c == ' ' || c == '\t') {   // skip space and tab
                n++;
                continue;
            }
            if (c == '\r' || c == '\n') {  // newline
                n++;
                if (c == '\r' && get<char>(n) == '\n') n++;  // "\r\n" windows newline
                if (comment == 1) comment = 0;                  // end comment
                if (n <= dataSize()) {
                    // finish current line
                    line.numTokens = tokens.numEntries() - line.firstToken;
                    line.linenum = linei++;
                    if (line.numTokens) {  // save line if not empty                  
                        lines.push(line);
                    }                    
                    // start next line
                    line.type = 0;
                    line.file = filei;
                    line.beginPos = n;
                    line.firstToken = tokens.numEntries();
                }
                continue;
            } 
            // illegal control character
            token.type = TOK_ERR;
            line.type = LINE_ERROR;
            comment = 1;              // ignore rest of line
            m = tokens.push(token);     // save error token
            errors.report(n, 1, ERR_CONTROL_CHAR);
        }
        // prepare token of any type
        token.pos  = n;
        token.stringLength = 1;
        token.id   = 0;
        //token.column = n - line.beginPos;
 
        // is it a name?
        if (!comment && nameChar1(c)) {
            // start of a name
            m = n+1;
            while (m < dataSize() && nameChar2(get<char>(m))) m++;
            // name goes from position n to m-1. make token
            token.type = TOK_NAM;
            token.pos = n;
            token.stringLength = m - n;
 
            // is it a register name
            f = isRegister((char*)buf()+n, token.stringLength);
            if (f) {
                token.type = TOK_REG;
                token.id = f;
            }
            // is it a keyword?
            if (token.type == TOK_NAM && m-n < sizeof(keywSearch.name)) {
                memcpy(keywSearch.name, buf()+n, m-n);
                keywSearch.name[m-n] = 0;
                f = keywords.findFirst(keywSearch);
                if (f >= 0) {  // keyword found
                    token.id = keywords[f].id;
                    token.type = keywords[f].id >> 24;
                    if (token.id == HLL_SWITCH) numSwitch++;
                }
            }
            // is it an instruction?
            if (token.type == TOK_NAM && m-n < sizeof(instructSearch.name)) {
                memcpy(instructSearch.name, buf()+n, m-n);
                instructSearch.name[m-n] = 0;
                f = instructionlistNm.findFirst(instructSearch);
                if (f >= 0) {  // instruction name found
                    token.type = TOK_INS;
                    token.id = instructionlistNm[f].id;
                }
            }
            n = m;
            tokens.push(token);     // save token
            continue;
        }
 
        // Is it a number?
        if (!comment) {
            bool isFloat;
            f = isNumber((char*)buf() + n, dataSize() - n, &isFloat);
            if (f) {
                token.type = TOK_NUM + isFloat;
                token.id = n;               // save number as string. The value is extracted later
                token.stringLength = f;
                n += f;
                tokens.push(token);     // save token
                continue;
            }
        } 
 
        // is it an operator?
        opSearch.name[0] = c;
        opSearch.name[1] = 0;
        f = operators.findFirst(opSearch);
        if (f >= 0) {
            // found single-character operator
            // make a greedy search for multi-character operators
            i = f;
            for (i = f+1; (uint32_t)i < operators.numEntries(); i++) {
                if (operators[i].name[0] != c) break;
                if (memcmp((char*)buf()+n, operators[i].name, strlen(operators[i].name)) == 0) f = i;
            }
            token.type = TOK_OPR;
            token.id = operators[f].id;
            token.priority = operators[f].priority;
            token.stringLength = (uint32_t)strlen(operators[f].name);
 
            // search for operators that need consideration here
            switch (token.id) {
 
            case 39: case '"':  // quoted string in single or double quotes
                if (comment) break;
                // search for end of string
                token.type = token.id == 39 ? TOK_CHA : TOK_STR;
                token.pos = n + 1;
                m = n;
                while (true) {
                    if (get<char>(m+1) == '\r' || get<char>(m+1) == '\n' || m == dataSize()) {
                        // end of line without matching end quote. multi-line quotes not allowed
                        token.type = TOK_ERR;
                        errors.report(token.pos-1, 1, ERR_QUOTE_BEGIN);
                        comment = 1; // skip rest of line
                        break;
                    }
                    if (get<char>(m+1) == c && get<char>(m) != '\\') {  // matching end quote not preceded by escape backslash
                        token.stringLength = m - n;
                        n += 2;
                        break;
                    }
                    m++;
                }
                break;
 
            case '/'+D2:            // "//". comment to end of line
                if (comment == 0) {                
                    comment = 1;    
                }
                break;
            case 'c':                                 // "/*" start of comment
                if (comment == 1) {
                    n += token.stringLength;         // skip and don't save token
                    continue;
                }
                if (comment == 2) {                     // nested comment
                    if (allowNestedComments) {
                        comment++;
                    }
                    else {
                        token.type = TOK_ERR;
                        errors.report(n, 2, ERR_COMMENT_BEGIN);
                    }
                    break;
                }
                comment = 2;
                commentStart = n;  commentStartColumn = n - line.beginPos;
                break;
            case 'd':                                // "*/" end of comment
                if (comment == 1) {
                    n += token.stringLength;         // skip and don't save token
                    continue;
                }
                if (comment == 2) {
                    comment = 0;
                    n += token.stringLength;         // skip and don't save token
                    continue;
                }
                else if (comment > 2 && allowNestedComments) {
                    comment--;
                    n += token.stringLength;         // skip and don't save token
                    continue;
                }
                else {
                    token.type = TOK_ERR;           // unmatched end comment
                    errors.report(n, 2, ERR_COMMENT_END);
                    comment = 1;
                }
                break;
            case ';':
                // semicolon starts a new pseudo-line
                if (comment) break;
                // finish current line
                tokens.push(token);     // the ';' token is used only in for(;;) loops. should be ignored at the end of the line otherwise
                n += token.stringLength;
                line.numTokens = tokens.numEntries() - line.firstToken;
                line.linenum = linei;
                if (line.numTokens) {  // save line if not empty                  
                    lines.push(line);
                }
                // start next line
                line.beginPos = n;
                line.firstToken = tokens.numEntries();
                continue;  // don't save ';' token twice
            case '{':  case '}':
                if (comment) break;
                // put each bracket in a separate pseudo-line to ease high level language parsing
                // finish current line
                line.numTokens = tokens.numEntries() - line.firstToken;
                line.linenum = linei; 
                if (line.numTokens) {  // save line if not empty                  
                    lines.push(line);
                }                    
                // start line with bracket only
                line.beginPos = n;
                line.firstToken = tokens.numEntries();
                tokens.push(token);      // save token
                n += token.stringLength;
                line.numTokens = 1;
                lines.push(line);
                // start line after bracket
                line.beginPos = n;
                line.firstToken = tokens.numEntries();
                continue;
            }
            if (comment == 0 && token.type != TOK_ERR) {            
                // save token unless we are inside a comment or an error has occurred           
                tokens.push(token);     // save token
            }
            n += token.stringLength;
            continue;
        }
 
        if (comment) {
            // we are inside a comment. Continue search only for end of line or end of comment
            n++;
            continue;
        }
 
        // none of the above. Make token for illegal character
        token.type = TOK_ERR;
        line.type = LINE_ERROR;
        errors.report(n, 1, ERR_ILLEGAL_CHAR);
        comment = 1;              // ignore rest of line
        n++;
    }
    // finish last line
   // tokens.push(token);
    line.numTokens = tokens.numEntries() - line.firstToken;
    lines.push(line);
    // start pseudo line
    line.beginPos = n;
    line.firstToken = tokens.numEntries();
    line.type = 0;
 
    // check for unmatched comment
    if (comment >= 2) {
        token.type = TOK_ERR;
        errors.report(commentStart, commentStartColumn, ERR_COMMENT_BEGIN);
    }
    // make EOF token in the end
    line.type = 0;
    line.beginPos = n; 
    line.firstToken = tokens.numEntries();
    line.numTokens = 1;
    lines.push(line);
    token.pos   = n;
    token.stringLength   = 0;
    token.type = TOK_EOF;    // end of file
    tokens.push(token);     // save eof token
}
 
 
void CAssembler::interpretSectionDirective() {
    // Interpret section directive during pass 2 or 3
    // pass 2: identify section name and type, and give it a number
    // pass 3: make section header
 
    // to do: nested sections
 
    uint32_t tok;                                          // token number
    ElfFWC_Sym2 sym;                                       // symbol record
    int32_t sectionsym = 0;                                // index to symbol record defining current section name
    uint32_t state = 0;                                    // 1: after align, 2: after '='
    ElfFwcShdr sectionHeader;                             // section header
    zeroAllMembers(sym);                                   // reset symbol
    zeroAllMembers(sectionHeader);                         // reset section header
    sectionHeader.sh_type = SHT_PROGBITS;                  // default section type
 
    sectionFlags = 0;
    for (tok = tokenB + 2; tok < tokenB + tokenN; tok++) { // get section attributes
        if (tokens[tok].type == TOK_ATT) {
            if (tokens[tok].id == ATT_UNINIT && state != 2) {
                sectionHeader.sh_type = SHT_NOBITS;                // uninitialized section (BSS)
                sectionFlags |= SHF_READ | SHF_WRITE;
            }
            else if (tokens[tok].id == ATT_COMDAT && state != 2) {
                sectionHeader.sh_type = SHT_COMDAT;                // communal section. duplicates and unreferenced sections are removed
            }
            else if (tokens[tok].id != ATT_ALIGN && state == 0) {
                sectionFlags |= tokens[tok].id & 0xFFFFFF;
                if (sectionFlags & SHF_EXEC) sectionFlags |= SHF_IP;  // executable section must be IP based
            }
            else if (tokens[tok].id == ATT_ALIGN && state == 0) {
                state = 1;
            }
            else {
                errors.report(tokens[tok]);  break;
            }
        }
        else if (tokens[tok].type == TOK_REG && tokens[tok].id == REG_IP    && state == 0)   sectionFlags |= SHF_IP;
        else if (tokens[tok].type == TOK_REG && tokens[tok].id == REG_DATAP && state == 0)   sectionFlags |= SHF_DATAP;
        else if (tokens[tok].type == TOK_REG && tokens[tok].id == REG_THREADP && state == 0) sectionFlags |= SHF_THREADP;
        else if (tokens[tok].type == TOK_OPR && tokens[tok].id == '=' && state == 1) state = 2;
        else if (tokens[tok].type == TOK_OPR && tokens[tok].id == ',' && state != 2) ; // comma, ignore
        else if (tokens[tok].type == TOK_NUM && state == 2) {
            if (pass >= 3) {  // alignment value
                uint32_t alignm = expression(tok, 1, 0).value.w;
                if ((alignm & (alignm - 1)) || alignm > MAX_ALIGN) errors.reportLine(ERR_ALIGNMENT);
                else {
                    sectionHeader.sh_align = bitScanReverse(alignm);
                }
            }
            state = 0;
        }
        else {
            errors.report(tokens[tok]);  break;
        }
    }
    // find or define symbol with section name
    sectionsym = findSymbol((char*)buf() + tokens[tokenB].pos, tokens[tokenB].stringLength);
    if (sectionsym <= 0) {
        // symbol not previously defined. Define it now
        sym.st_type = STT_SECTION;
        sym.st_name = symbolNameBuffer.putStringN((char*)buf() + tokens[tokenB].pos, tokens[tokenB].stringLength);
        sym.st_bind = sectionFlags;
        sectionsym = addSymbol(sym);         // save symbol with section name
    }
    else {
        // symbol already defined. check that it is a section name
        if (symbols[sectionsym].st_type != STT_SECTION) {
            errors.report(tokens[tokenB].pos, tokens[tokenB].stringLength, ERR_SYMBOL_DEFINED);
        }
    }
    sectionFlags |= SHF_ALLOC;
    lines[linei].type = LINE_SECTION;                          // line is section directive
    lines[linei].sectionType = sectionFlags;
    if (symbols[sectionsym].st_section == 0) {
        // new section. make section header
        sectionHeader.sh_name = symbols[sectionsym].st_name;
        if (sectionFlags & SHF_EXEC) {
            sectionHeader.sh_entsize = 4;
            if (sectionHeader.sh_align < 2) sectionHeader.sh_align = 2;
            sectionFlags |= SHF_IP;            
        }
        else { // data section
            if (!(sectionFlags & (SHF_READ | SHF_WRITE))) sectionFlags |= SHF_READ | SHF_WRITE; // read or write attributes not specified, default is both
            if (!(sectionFlags & (SHF_IP | SHF_DATAP | SHF_THREADP))) {  // address reference not specified. assume datap if writeable, ip if readonly
                if (sectionFlags & SHF_WRITE) sectionFlags |= SHF_DATAP;
                else sectionFlags |= SHF_IP;
            }
        }
        sectionHeader.sh_flags = sectionFlags;
        section = sectionHeaders.push(sectionHeader);
        symbols[sectionsym].st_section = section;
    }
    else {  // this section is seen before
        section = symbols[sectionsym].st_section;
        if (sectionHeaders[section].sh_align < sectionHeader.sh_align) sectionHeaders[section].sh_align = sectionHeader.sh_align;
        if (sectionFlags && (sectionFlags & ~sectionHeaders[section].sh_flags)) errors.reportLine(ERR_SECTION_DIFFERENT_TYPE);
        sectionFlags = (uint32_t)sectionHeaders[section].sh_flags;
        if (sectionHeader.sh_align > 2) {
            // insert alignment code
            SCode code;
            zeroAllMembers(code);
            code.instruction = II_ALIGN;
            code.value.u = (int64_t)1 << sectionHeader.sh_align;
            code.sizeUnknown = 0x80;
            code.section = section;
            codeBuffer.push(code);
        }
    }
}
 
void CAssembler::interpretFunctionDirective() {
    // Interpret function directive during pass 2
    uint32_t tok;                     // token number
    ElfFWC_Sym2 sym;                  // symbol record
    zeroAllMembers(sym);              // reset symbol
    int32_t symi;
 
    symi = findSymbol((char*)buf() + tokens[tokenB].pos, tokens[tokenB].stringLength);
    if (symi > 0) {
        if (pass == 2) errors.report(tokens[tokenB].pos, tokens[tokenB].stringLength, ERR_SYMBOL_DEFINED);  // symbol already defined
    }
    else {
        // define symbol
        sym.st_type = STT_FUNC;
        sym.st_other = STV_IP;
        sym.st_name = symbolNameBuffer.putStringN((char*)buf() + tokens[tokenB].pos, tokens[tokenB].stringLength);
        sym.st_bind = 0;
        sym.st_section = section;
        for (tok = tokenB + 2; tok < tokenB + tokenN; tok++) { // get function attributes
            if (tokens[tok].type == TOK_OPR && tokens[tok].id == ',') continue;
            if (tokens[tok].id == ATT_WEAK) sym.st_bind |= STB_WEAK;             
            if (tokens[tok].id == ATT_REGUSE) {
                if (tokens[tok+1].id == '=' && tokens[tok+2].type == TOK_NUM) {
                    tok += 2;
                    sym.st_reguse1 = expression(tok, 1, 0).value.w;
                    sym.st_other |= STV_REGUSE;
                    if (tokens[tok+1].id == ',' && tokens[tok+2].type == TOK_NUM) {
                        tok += 2;
                        sym.st_reguse2 = expression(tok, 1, 0).value.w;
                    }
                }             
            }
            else if (tokens[tok].type == TOK_DIR && tokens[tok].id == DIR_PUBLIC) sym.st_bind |= STB_GLOBAL;
            else {
                errors.report(tokens[tok]);  // unexpected token
            }
        }
        symi = addSymbol(sym);          // save symbol with function name
    }
    lines[linei].type = LINE_FUNCTION;           // line is function directive
 
    if (pass == 3 && symi) {
        // make a label here. The final address will be calculated in pass 4
        SCode code;                              // current instruction code
        zeroAllMembers(code);                    // reset code structure
        code.label = symbols[symi].st_name;
        code.section = section;
        codeBuffer.push(code);
    }
}
 
void CAssembler::interpretEndDirective() {
    // Interpret section or function end directive during pass 2
    ElfFWC_Sym2 sym;                  // symbol record
    zeroAllMembers(sym);              // reset symbol
    int32_t symi;
    CTextFileBuffer tempBuffer;       // temporary storage of names
 
    symi = findSymbol((char*)buf() + tokens[tokenB].pos, tokens[tokenB].stringLength);
    if (symi <= 0) {
        errors.reportLine(ERR_UNMATCHED_END);
    }
    else {
        if (symbols[symi].st_type == STT_SECTION) {
            if (symbols[symi].st_section == section) {
                // current section ends here
                section = 0;  sectionFlags = 0;
            }
            else {
                errors.reportLine(ERR_UNMATCHED_END);
            }
        }
        else if (symbols[symi].st_type == STT_FUNC && pass >= 4) {
            symbols[symi].st_unitsize = 4;  
            // to do: insert size!
            //symbols[symi].st_unitsize = ?
            // support function(){} syntax. prevent nested functions
        }
    }
    lines[linei].type = LINE_ENDDIR;        // line is end directive
}
 
// Interpret line specifying options
void CAssembler::interpretOptionsLine() {
 
    // Expecting a line of the type:
    // "options codesize = 0x10000, datasize = 1 << 20"
    uint32_t tok;                      // token number
    uint32_t state = 0;                // 0: start, 1: after option name, 2: after equal sign, 3: after expression
    const char * optionname = 0;
    int option = 0;                    // 1: codesize, 2: datasize
    SExpression val;                   // value to be assigned
    SCode code;                        // instruction code containing options
    for (tok = tokenB + 1; tok < tokenB + tokenN; tok++) { 
 
        switch (state) {
        case 0:                        // start. expect name "datasize" or "codesize"
            if (tokens[tok].type != TOK_NAM) {
                errors.report(tokens[tok]);  return;  // unexpected token
            }
            optionname = (char*)buf()+tokens[tok].pos; // tokens[tok].stringLength;
            if (strncasecmp_(optionname, "codesize", 8) == 0) option = 1;
            else if (strncasecmp_(optionname, "datasize", 8) == 0) option = 2;
            else {
                errors.report(tokens[tok]);  return;  // unexpected name
            }
            state = 1;
            break;
 
        case 1:  // after name, expecting equal sign
            if (tokens[tok].type == TOK_OPR && tokens[tok].id == '=') {
                state = 2;
            }
            else {
                errors.report(tokens[tok]);  return;  // unexpected token
            }
            break;
 
        case 2:  // expect expression
            val = expression(tok, tokenB + tokenN - tok, 0);  // evaluate number or expression
            tok += val.tokens - 1;
            if (val.etype != XPR_INT) {
                errors.reportLine(ERR_MUST_BE_CONSTANT);
                return;
            }
            zeroAllMembers(code);                    // reset code structure
            switch (option) {
            case 1:  // set codesize
                if (val.value.u == 0) code_size = cmd.codeSizeOption;
                else code_size = val.value.u; 
                code.value.u = code_size;
                break;
            case 2:  // set datasize 
                if (val.value.u == 0) data_size = cmd.dataSizeOption;
                else data_size = val.value.u;
                code.value.u = data_size;
                break;
            }
            // This is called only in pass 3. Save this option for pass 4:
            code.instruction = II_OPTIONS;
            code.section = section;
            code.fitNum = option;
            code.sizeUnknown = 1;
            codeBuffer.push(code);
            state = 3;
            break;
 
        case 3:  // expect comma or nothing
            if (tokens[tok].type == TOK_OPR && tokens[tok].id == ',') {
                state = 0;    // start over after comma
            }
            else {
                errors.report(tokens[tok]);  return;  // unexpected token
            }
        }
    }
}
 
 
// Find symbol by index into symbolNameBuffer. The return value is an index into symbols. 
// Symbol indexes may change when new symbols are added to the symbols list, which is sorted by name
uint32_t CAssembler::findSymbol(uint32_t namei) {
    ElfFWC_Sym2 sym;                                       // temporary symbol record used for searching
    sym.st_name = namei;
    return symbols.findFirst(sym);                         // find symbol by name
} 
 
// Find symbol by name as string. The return value is an index into symbols. 
// Symbol indexes may change when new symbols are added to the symbols list, which is sorted by name
uint32_t CAssembler::findSymbol(const char * name, uint32_t len) {
    uint32_t saveSize = symbolNameBuffer.dataSize();       // save symbolNameBuffer size for later reset
    uint32_t namei = symbolNameBuffer.putStringN(name, len); // put name temporarily into symbolNameBuffer
    int32_t symi = findSymbol(namei);                      // find symbol by name index
    symbolNameBuffer.setSize(saveSize);                    // remove temporary name from symbolNameBuffer
    return symi;                                           // return symbol index
}
 
// Add a symbol to symbols list
uint32_t CAssembler::addSymbol(ElfFWC_Sym2 & sym) {
    int32_t f = symbols.findFirst(sym);
    if (f >= 0) {
        // error: symbol already defined
        return 0;
    }
    else {
        return symbols.addUnique(sym);
    }
}
 
// interpret   name: options {, name: options}
void CAssembler::interpretExternDirective() {
    uint32_t tok;                     // token number
    uint32_t nametok = 0;             // last name token
    ElfFWC_Sym2 sym;                  // symbol record
    zeroAllMembers(sym);              // reset symbol
    sym.st_bind = STB_GLOBAL;
 
    // Example: extern name1: int32 weak, name2: function, name3, name4: read 
    uint32_t state = 0;  // 0: after extern or comma, 
                         // 1: after name, 
                         // 2: after colon
 
    // loop through tokens on this line
    for (tok = tokenB + 1; tok < tokenB + tokenN; tok++) {
        switch (state) {
        case 0:  // after extern or comma. expecting name
            if (tokens[tok].type == TOK_NAM) {
                // name encountered
                sym.st_name = symbolNameBuffer.putStringN((char*)buf()+tokens[tok].pos, tokens[tok].stringLength);
                state = 1;  nametok = tok;
            }
            else errors.report(tokens[tok]);
            break;
        case 1: // after name. expecting colon or comma
            if (tokens[tok].type == TOK_OPR) {
                if (tokens[tok].id == ':') {
                    state = 2;
                    continue;
                }
                else if (tokens[tok].id == ',') {
                    goto COMMA;
                }
            }
            errors.report(tokens[tok]);
            break;
        case 2:  // after colon. expecting attribute or comma or end of line
            if (tokens[tok].type == TOK_TYP) {
                // symbol size given by type token
                uint32_t s = tokens[tok].id & 0xF;
                if (s > 4) s -= 3;  // float types
                sym.st_unitsize = uint32_t(1 << s);
                sym.st_unitnum = 1;
            }
            else if (tokens[tok].type == TOK_ATT || tokens[tok].type == TOK_DIR) {
                ATTRIBUTE:
                switch (tokens[tok].id) {
                case DIR_FUNCTION: case ATT_EXEC: // function or execute
                    if (sym.st_type) {
                        errors.report(tokens[tok].pos, tokens[tok].stringLength, ERR_CONFLICT_TYPE);
                    } 
                    sym.st_type = STT_FUNC;
                    sym.st_other = STV_IP | STV_EXEC;
                    break;
                case ATT_READ:  // read
                    if (sym.st_type == 0) sym.st_other |= STV_READ;
                    break;
                case ATT_WRITE:  // write
                    if (sym.st_type == STT_FUNC) {
                        errors.report(tokens[tok].pos, tokens[tok].stringLength, ERR_CONFLICT_TYPE);
                    } 
                    else {
                        sym.st_type = STT_OBJECT;
                    }
                    break;
                case ATT_WEAK:   // weak
                    sym.st_bind = STB_WEAK;
                    break;
                case ATT_CONSTANT:  // constant
                    sym.st_type = STT_CONSTANT;
                    break;
                case ATT_REGUSE:
                    if (tokens[tok+1].id == '=' && (tokens[tok+2].type == TOK_NUM /*|| tokens[tok+2].type == TOK_OPR)*/)) {
                        tok += 2;
                        sym.st_reguse1 = expression(tok, 1, 0).value.w;
                        sym.st_other |= STV_REGUSE;
                        if (tokens[tok+1].id == ',' && tokens[tok+2].type == TOK_NUM) {
                            tok += 2;
                            sym.st_reguse2 = expression(tok, 1, 0).value.w;
                        }
                    }
                    break;
                default:  // error
                    errors.report(tokens[tok]);
                }
            }
            else if (tokens[tok].type == TOK_REG) {
                switch (tokens[tok].id) {
                case REG_IP:  
                    sym.st_other |= STV_IP;  break;
                case REG_DATAP:
                    sym.st_other |= STV_DATAP;  break;
                case REG_THREADP:
                    sym.st_other |= STV_THREADP;  break;
                default: errors.report(tokens[tok]);
                }
            }
            else if (tokens[tok].type == TOK_OPR && tokens[tok].id == ',') {
                // end of definition. save symbol
            COMMA:
                if (tok < tokenB + tokenN 
                    && (tokens[tok + 1].type == TOK_ATT || tokens[tok + 1].type == TOK_DIR)) {
                    tok++; goto ATTRIBUTE;
                }
                uint32_t symi = addSymbol(sym);          // save symbol with function name
                if (symi == 0) {  // symbol already defined
                    errors.report(tokens[nametok].pos, tokens[nametok].stringLength, ERR_SYMBOL_DEFINED);
                }
                sym.st_name = 0;          // clear record for next symbol
                sym.st_type = 0;
                sym.st_other = 0;
                sym.st_unitsize = 0;
                sym.st_unitnum = 0;
                sym.st_bind = STB_GLOBAL;
                state = 0;
            }
            else {
                errors.report(tokens[tok]);
            }
            break;
        }
    }
    if (state) {  // last extern definition does not end with comma. finish it here
        goto COMMA;
    }
    lines[linei].type = LINE_DATADEF;        // line is data definition
}
 
 
void CAssembler::interpretLabel(uint32_t tok) {
    // line begins with a name. interpret label
    // to do: add type if data. not string type
    ElfFWC_Sym2 sym;   // symbol record
    zeroAllMembers(sym); // reset symbol
 
    // save name
    sym.st_name = symbolNameBuffer.putStringN((char*)buf()+tokens[tok].pos, tokens[tok].stringLength);
    sym.st_section = section;
    // determine if code or data from section type
    if (sectionFlags & SHF_EXEC) {
        sym.st_type = STT_FUNC;
        sym.st_other = STV_EXEC | STV_IP;
    }
    else {
        sym.st_type = STT_OBJECT;
        sym.st_other = sectionFlags & STV_SECT_ATTR;
    }
 
    // look for more exact type information
    if (tokenN > 2) {
        uint32_t t = tok+2;
        if (tokens[t].type == TOK_TYP) {
            uint32_t s = tokens[t].id & 0xF;
            if (s > 4) s -= 3;
            sym.st_unitsize = uint32_t(1 << s);
            sym.st_unitnum = 1;
            if (tokenN > 3) t++;
        }
        if (tokens[t].type == TOK_NUM || tokens[t].type == TOK_FLT) {
            sym.st_type = STT_OBJECT;
            lines[linei].type = LINE_DATADEF;
        }
        else if (tokens[t].type == TOK_REG || tokens[t].type == TOK_INS || tokens[t].id == '[') {
            lines[linei].type = LINE_CODEDEF;
            sym.st_type = STT_FUNC;
        }
    }
    if (section) { // copy type info from section
        sym.st_other = sectionHeaders[section].sh_flags & STV_SECT_ATTR;
    }
 
    if (lines[linei].type == 0) {
        lines[linei].type = (sectionFlags & SHF_EXEC) ? LINE_CODEDEF : LINE_DATADEF;
    }
 
    uint32_t symi = addSymbol(sym);     // add symbol to symbols list
 
    if (section) {
        // symbol address
        symbols[symi].st_value = sectionHeaders[section].sh_size;
    }
    tokens[tok].id = symbols[symi].st_name;         // save symbol name index
    if (symi == 0) errors.report(tokens[tokenB].pos, tokens[tokenB].stringLength, ERR_SYMBOL_DEFINED);
}
 
 
// interpret assembly style variable definition:
// label: type value1, value2
void CAssembler::interpretVariableDefinition1() {
    int state = 0;      // 0: start
                        // 1: after label
                        // 2: after :
                        // 3: after type or ,
                        // 4: after value
    uint32_t tok;                      // token index
    uint32_t type = 0;                 // data type
    uint32_t dsize = 0;                // data size
    uint32_t dsize1;                   // log2(dsize)
    uint32_t dnum = 0;                 // number of data items
    uint32_t stringlen = 0;            // length of string
    uint32_t symi = 0;                 // symbol index
    ElfFWC_Sym2 sym;                   // symbol record
    zeroAllMembers(sym);               // reset symbol
    SExpression exp1;                  // expression when interpreting numeric expression
 
    if (section == 0) {
        errors.reportLine(ERR_DATA_WO_SECTION);
    }
 
    // loop through tokens on this line
    for (tok = tokenB; tok < tokenB + tokenN; tok++) {
        switch (state) {
        case 0:  // start
            if (tokens[tok].type == TOK_NAM) { // name. make symbol
                sym.st_name = symbolNameBuffer.putStringN((char*)buf()+tokens[tok].pos, tokens[tok].stringLength);
                sym.st_type = STT_OBJECT;
                symi = symbols.addUnique(sym);
                tokens[tok].type = TOK_SYM;      // change token type
                tokens[tok].id = symbols[symi].st_name;  // use name offset as unique identifier because symbol index can change
                state = 1;
            }
            else if (tokens[tok].type == TOK_SYM) { // symbol
                symi = findSymbol(tokens[tok].id);
                if (symi > 0) {
                    if (pass == 2) errors.report(tokens[tok].pos, tokens[tok].stringLength, ERR_SYMBOL_DEFINED);  // symbol already defined
                }
                state = 1;
            }
            else if (tokens[tok].type == TOK_TYP) {
                goto TYPE_TOKEN;
            }
            else errors.report(tokens[tok]);
            if (symi && section) {
                symbols[symi].st_value = sectionHeaders[section].sh_size;
            }            
            break;
        case 1:  // after label. expect colon
            if (tokens[tok].type == TOK_OPR && tokens[tok].id == ':') {
                state = 2;
            }
            else errors.report(tokens[tok].pos, tokens[tok].stringLength, ERR_EXPECT_COLON);
            break;
        case 2:  // expect type
            if (tokens[tok].type == TOK_TYP) {
                TYPE_TOKEN:
                type = tokens[tok].id & 0xFF;
                dsize1 = type & 0xF;
                if (type & 0x40) dsize1 -= 3;
                dsize = 1 << dsize1;
                state = 3; 
                if (section) {  // align data
                    uint32_t addr = (uint32_t)sectionHeaders[section].sh_size;
                    if (sectionHeaders[section].sh_align < dsize1) sectionHeaders[section].sh_align = dsize1;  // update section alignment
                    if (addr & (dsize - 1)) { // needs to insert zeroes
                        uint32_t addr2 = (addr + dsize - 1) & -(int32_t)dsize;
                        sectionHeaders[section].sh_size = addr2;           // update address
                        if (symi) symbols[symi].st_value = addr2;          // update symbol address
                        if (pass >= 3) {                        
                            dataBuffers[section].align((uint32_t)dsize);   // put zeroes in data buffer
                        }                        
                    }
                }
            }
            else errors.report(tokens[tok]);
            break;
        case 3:  // after type. expect value. evaluate expression
            exp1 = expression(tok, tokenB + tokenN - tok, pass < 3 ? 0x10 : 0); // pass 3: may contain symbols not defined yet
            tok += exp1.tokens - 1;
            if (exp1.etype & XPR_STRING) {  // string expression: get size
                if ((type & 0x1F) != (TYP_INT8 & 0x1F)) errors.reportLine(ERR_STRING_TYPE);  // string must use type int8
                stringlen = exp1.sym2; // string length
            }
            else stringlen = 0;
            if (pass < 3) {
                if (section) sectionHeaders[section].sh_size += stringlen ? stringlen : dsize;  // update address
            }
            else {
                if (section) {
                    // save data of desired type
                    if (exp1.etype & XPR_FLT) { 
                        // floating point number specified
                        if ((type & 0xF0) == (TYP_INT8 & 0xF0)) {  // float specified, integer expected
                            exp1.value.i = int64_t(exp1.value.d);
                            errors.reportLine(ERR_CONFLICT_TYPE);
                        }
                    }
                    else if (exp1.etype & XPR_INT) { 
                        if (type & TYP_FLOAT) {  // integer specified, float expected
                            exp1.value.d = double(exp1.value.i);  // convert to float
                        }
                    }
                    int64_t value = exp1.value.i;  //value of expression
                    if (exp1.sym3) {                                                                                      
                        // calculation of symbol value. add relocation if needed
                        uint32_t size = type & 0xF;
                        if (type & 0x40) size -= 3;
                        size = 1 << size;
                        //value = calculateConstantOperand(exp1, dataBuffers[section].dataSize(), size);                            
                        value = calculateConstantOperand(exp1, sectionHeaders[section].sh_size, dsize);                            
                        if (exp1.etype & XPR_ERROR) {
                            errors.reportLine((uint32_t)value); // report error
                            break;
                        }
                        // check for overflow
                        bool overflow = false;
                        switch (type & 0xFF) {
                        case TYP_INT8 & 0xFF:
                            overflow = value > 0x7F || value < -0x80;
                            break;
                        case TYP_INT16 & 0xFF:
                            overflow = value > 0x7FFF || value < -0x8000;
                            break;
                        case TYP_INT32 & 0xFF:
                            overflow = value > 0x7FFFFFFF || value < int32_t(0x80000000);
                            break;
                        default:;
                        }
                        if (overflow) errors.reportLine(ERR_OVERFLOW); // (symbol1 - symbol2) overflows
                    }
                    if (sectionHeaders[section].sh_type == SHT_NOBITS) {
                        // uninitialized (BSS) section. check that value is zero, but don't store
                        if (value != 0) errors.reportLine(ERR_NONZERO_IN_BSS); // not zero
                    }
                    else {
                        // save data
                        switch (type & 0xFF) {
                        case TYP_INT8 & 0xFF:
                            if (stringlen) {
                                dataBuffers[section].push(stringBuffer.buf() + exp1.value.w, stringlen);
                                break;
                            }
                            dataBuffers[section].push(&value, 1);  break;
                        case TYP_INT16 & 0xFF:
                            dataBuffers[section].push(&value, 2);  break;
                        case TYP_INT32 & 0xFF:
                            dataBuffers[section].push(&value, 4);  break;
                        case TYP_INT64 & 0xFF:
                            dataBuffers[section].push(&value, 8);  break;
                        case TYP_INT128 & 0xFF:
                            dataBuffers[section].push(&value, 8);
                            value = value >> 63;     // sign extend
                            dataBuffers[section].push(&value, 8);
                            break;
                        case TYP_FLOAT16 & 0xFF:  // half precision
                            exp1.value.w = double2half(exp1.value.d);
                            dataBuffers[section].push(&exp1.value.w, 2);  break;
                        case TYP_FLOAT32 & 0xFF: { // single precision
                            float val = float(exp1.value.d);
                            dataBuffers[section].push(&val, 4); }
                            break;
                        case TYP_FLOAT64 & 0xFF:  // double precision
                            dataBuffers[section].push(&exp1.value.d, 8);  break;
                        }
                    }
                    sectionHeaders[section].sh_size += stringlen ? stringlen : dsize;  // update address
                }
            }
            if (!(exp1.etype & (XPR_IMMEDIATE | XPR_STRING | XPR_SYM1 | XPR_UNRESOLV)) || (exp1.etype & (XPR_REG|XPR_OPTION|XPR_MEM|XPR_ERROR))) errors.report(tokens[tok]);
 
            if (stringlen) dnum += stringlen; else dnum += 1;
            state = 4;
            break;
        case 4:  // after value. expect comma or end of line
            if (tokens[tok].type == TOK_OPR && tokens[tok].id == ',') {
                state = 3;
            }
            else errors.report(tokens[tok]);
            break;
        }
        if (lineError) return;
    }
    if (state != 4 && state != 2) errors.report(tokens[tok-1]);
    if (symi) { // save size
        symbols[symi].st_unitsize = dsize;
        symbols[symi].st_unitnum = dnum;
        symbols[symi].st_section = section;
        if ((type & 0xF0) == (TYP_FLOAT32 & 0xF0)) symbols[symi].st_other |= STV_FLOAT;
        if (section) { // copy information from section
            symbols[symi].st_other |= sectionHeaders[section].sh_flags & STV_SECT_ATTR;
        }
    }
}
 
// interpret C style variable definition:
// type name1 = value1, name2[num] = {value, value, ..}
void CAssembler::interpretVariableDefinition2() {
    int state = 0;      // 0: start
                        // 1: after type or comma
                        // 2: after name
                        // 3: after [
                        // 4: after [number
                        // 5: after =
                        // 6: after = number
                        // 7: after {
                        // 8: after {number
 
    uint32_t tok;                           // token index
    uint32_t dsize = 0;                     // data element size
    uint32_t dsize1 = 0;                    // data element size = 1 << dsize1
    uint32_t type = 0;                      // data type
    uint32_t arrayNum1 = 1;                 // number of elements indicated in []
    uint32_t arrayNum2 = 0;                 // number of elements in {} list
    uint32_t stringlen = 0;                 // length of string
    uint32_t symi = 0;                      // symbol index
    ElfFWC_Sym2 sym;                        // symbol record
    zeroAllMembers(sym);                    // reset symbol
    SExpression exp1;                       // expression when interpreting numeric expression
 
    if (section == 0) {
        errors.reportLine(ERR_DATA_WO_SECTION);
    }
 
    // loop through tokens on this line
    for (tok = tokenB; tok < tokenB + tokenN; tok++) {
        switch (state) {
        case 0:  // this is a type token
            type  = tokens[tok].id & 0xFF;
            dsize1 = tokens[tok].id & 0xF;  
            if ((type & 0x40) > 3) dsize1 -= 3;
            dsize = 1 << dsize1;
            state = 1;
            if (section) {  // align data
                uint32_t addr = (uint32_t)sectionHeaders[section].sh_size;
                if (addr & (dsize - 1)) { // needs to insert zeroes
                    uint32_t addr2 = (addr + dsize - 1) & -(int32_t)dsize;  // calculate aligned address
                    sectionHeaders[section].sh_size = addr2;           // update address
                    if (pass >= 3) {                    
                        dataBuffers[section].align(dsize);   // put zeroes in data buffer
                    }
                }
                if (sectionHeaders[section].sh_align < dsize1) sectionHeaders[section].sh_align = dsize1;  // update section alignment
            }
            break;
        case 1:  // expecting name token. save name
            if (tokens[tok].type == TOK_NAM) { // name. make symbol
                sym.st_name = symbolNameBuffer.putStringN((char*)buf()+tokens[tok].pos, tokens[tok].stringLength);
                symi = addSymbol(sym);
                if (symi == 0 && pass == 2) {
                    errors.report(tokens[tok].pos, tokens[tok].stringLength, ERR_SYMBOL_DEFINED);  break;
                }
                symbols[symi].st_type = (sectionFlags & SHF_EXEC) ? STT_FUNC : STT_OBJECT;
                tokens[tok].type = TOK_SYM;      // change token type
                tokens[tok].id = symbols[symi].st_name;  // use name offset as unique identifier because symbol index can change
                state = 2;
            }
            else if (tokens[tok].type == TOK_SYM) { // symbol
                symi = findSymbol(tokens[tok].id);
                if (symi > 0 && pass == 2) errors.report(tokens[tok].pos, tokens[tok].stringLength, ERR_SYMBOL_DEFINED);  // symbol already defined
                state = 2;
            }
            else {
                errors.report(tokens[tok]);
            }
            //nametok = tok;
            symbols[symi].st_unitsize = dsize;
            symbols[symi].st_unitnum = 0;
 
            if ((type & 0xF0) == (TYP_FLOAT32 & 0xF0)) symbols[symi].st_other |= STV_FLOAT;
            if (section) { // copy information from section
                symbols[symi].st_value = sectionHeaders[section].sh_size;
                symbols[symi].st_other |= sectionHeaders[section].sh_flags & STV_SECT_ATTR;
            }
            break;
        case 2:  // after name. expect , = [ eol
            if (tokens[tok].type != TOK_OPR) {
                errors.report(tokens[tok]);  break;
            }
            switch (tokens[tok].id) {
            case ',':  // finish this symbol definition
                COMMA:
                    if (arrayNum2 > arrayNum1) { // check if the two array sizes match
                        if (arrayNum1 > 1) {
                            errors.report(tokens[tok-1].pos, tokens[tok-1].stringLength, ERR_CONFLICT_ARRAYSZ);
                        } 
                        else arrayNum1 = arrayNum2;
                    }
                    symbols[symi].st_unitsize = dsize;
                    symbols[symi].st_unitnum = arrayNum1;
                    symbols[symi].st_reguse1 = linei;
                    symbols[symi].st_section = section;
 
                    if (arrayNum1 > arrayNum2 && section) {
                        // unspecified elements are zero. calculate extra size
                        uint32_t asize = (arrayNum1 - arrayNum2) * dsize;
                        sectionHeaders[section].sh_size += asize;
                        if (pass >= 3 && sectionHeaders[section].sh_type != SHT_NOBITS) {
                            // store any unspecified elements as zero
                            uint64_t zero = 0;
                            while (asize > 8) {
                                dataBuffers[section].push(&zero, 8);  asize -= 8;
                            }
                            while (asize > 0) {
                                dataBuffers[section].push(&zero, 1);  asize -= 1;
                            }
                        }
                    }
 
                    // get ready for next symbol
                    zeroAllMembers(sym);
                    arrayNum1 = 1;  arrayNum2 = 0;
                    if (state == 99) return;       // finished line
                    state = 1;
                    break;                
            case '=':
                state = 5;
                break;
            case '[':
                state = 3;
                break;
            default:
                errors.report(tokens[tok]);
            }
            break;
        case 3:  // after [ . expect number or ]
            if (tokens[tok].id == ']') {
                state = 2; break;
            }
            if (arrayNum1 > 1) {
                errors.report(tokens[tok].pos, tokens[tok].stringLength, ERR_MULTIDIMENSIONAL);  break;            // error. multidimensional array not supported
            } 
            // evaluate numeric expression inside []. 
            // it may contain complex expressions that can only be evaluated later, but
            // this will not generate an error message here
            exp1 = expression(tok, tokenB + tokenN - tok, 0x10);
            if (lineError) return;
            tok += exp1.tokens -1;
            if (exp1.etype == 0) errors.report(tokens[tok]);
            if ((exp1.etype & ~XPR_IMMEDIATE) == 0) {
                arrayNum1 = exp1.value.w;
            }
            state = 4;
            break;
        case 4:  // after [number. expect ]
            if (tokens[tok].id != ']') {
                errors.report(tokens[tok]);  break;
            }
            state = 2;
            break;
        case 5:  // after =. expect number or {numbers}
            if (tokens[tok].id == '{') state = 7;
            else {
                state = 6;
                goto SAVE_VALUE;  // interpret value and save it
            }
            break;
        case 6:  // after = number. expect comma or eol
            if (tokens[tok].id != ',') {
                errors.report(tokens[tok]);  break;
            }
            goto COMMA;
        case 7:  // after {. expect number list
            state = 8;
        SAVE_VALUE:
            arrayNum2++;
            if (pass < 3) {
                // may contain symbols not defined yet. just pass expression and count tokens
                exp1 = expression(tok, tokenB + tokenN - tok, 0x10);
                tok += exp1.tokens - 1;
                if (lineError) return;
            }
            else {
                // pass 5. evaluate expression and save value
                exp1 = expression(tok, tokenB + tokenN - tok, 0);
                tok += exp1.tokens - 1;
                if (lineError) return;
                if ((exp1.etype & XPR_SYM1) && exp1.sym3 && pass > 3) {                                                                                      
                    // calculation of symbol value. add relocation if needed
                    exp1.value.i = calculateConstantOperand(exp1, sectionHeaders[section].sh_size, dsize);                            
                    if (exp1.etype & XPR_ERROR) {
                        errors.reportLine((uint32_t)(exp1.value.i)); // report error
                        break;
                    }
                    // check for overflow
                    bool overflow = false;
                    switch (type & 0xFF) {
                    case TYP_INT8 & 0xFF:
                        overflow = exp1.value.i > 0x7F || exp1.value.i < -0x80;
                        break;
                    case TYP_INT16 & 0xFF:
                        overflow = exp1.value.i > 0x7FFF || exp1.value.i < -0x8000;
                        break;
                    case TYP_INT32 & 0xFF:
                        overflow = exp1.value.i > 0x7FFFFFFF || exp1.value.i < int32_t(0x80000000);
                        break;
                    default:;
                    }
                    if (overflow) errors.reportLine(ERR_OVERFLOW); // (symbol1 - symbol2) overflows
                }
            }
            if (!(exp1.etype & (XPR_IMMEDIATE | XPR_STRING | XPR_UNRESOLV | XPR_SYM1)) || (exp1.etype & (XPR_REG|XPR_OPTION|XPR_MEM|XPR_ERROR))) {
                errors.report(tokens[tok]);
            }
            if (section && section < dataBuffers.numEntries() && pass >= 3) {
                // save data of desired type
                if ((exp1.etype & XPR_IMMEDIATE) == XPR_FLT) { 
                    // floating point number specified
                    if ((type & 0xF0) == (TYP_INT8 & 0xF0)) {  // float specified, integer expected
                        exp1.value.i = int64_t(exp1.value.d);
                        errors.reportLine(ERR_CONFLICT_TYPE);
                    }
                }
                else if ((exp1.etype & XPR_IMMEDIATE) == XPR_INT) { 
                    if ((type & 0xF0) == (TYP_FLOAT32 & 0xF0)) {  // integer specified, float expected
                        exp1.value.d = double(exp1.value.i);  // convert to float
                    }
                }
                else if (exp1.etype & XPR_STRING) {  // string expression: get size
                    if ((type & 0x1F) != (TYP_INT8 & 0x1F)) errors.reportLine(ERR_STRING_TYPE);  // string must use type int8
                    stringlen = exp1.sym2; // string length
                }
                else stringlen = 0;
 
                if (sectionHeaders[section].sh_type == SHT_NOBITS) {
                    // uninitialized (BSS) section. check that value is zero, but don't store
                    if (exp1.value.i != 0) errors.reportLine(ERR_NONZERO_IN_BSS); // not zero
                }
                else {
                    // save data
                    switch (type & 0xFF) {
                    case TYP_INT8 & 0xFF:
                        if (stringlen) {
                            dataBuffers[section].push(stringBuffer.buf() + exp1.value.w, stringlen);
                            break;
                        }
                        dataBuffers[section].push(&exp1.value.u, 1);  break;
                    case TYP_INT16 & 0xFF:
                        dataBuffers[section].push(&exp1.value.u, 2);  break;
                    case TYP_INT32 & 0xFF:
                        dataBuffers[section].push(&exp1.value.u, 4);  break;
                    case TYP_INT64 & 0xFF:
                        dataBuffers[section].push(&exp1.value.u, 8);  break;
                    case TYP_INT128 & 0xFF:
                        dataBuffers[section].push(&exp1.value.u, 8);
                        exp1.value.i = exp1.value.i >> 63;     // sign extend
                        dataBuffers[section].push(&exp1.value.u, 8);
                        break;
                    case TYP_FLOAT16 & 0xFF:  // half precision
                        exp1.value.w = double2half(exp1.value.d);
                        dataBuffers[section].push(&exp1.value.w, 2);  break;
                    case TYP_FLOAT32 & 0xFF: { // single precision
                        float val = float(exp1.value.d);
                        dataBuffers[section].push(&val, 4); }
                                             break;
                    case TYP_FLOAT64 & 0xFF:  // double precision
                        dataBuffers[section].push(&exp1.value.d, 8);  break;
                    }
                }
            }
            sectionHeaders[section].sh_size += stringlen ? stringlen : dsize;  // update address
            break;
        case 8:  // after {number. expect comma or }
            if (tokens[tok].id == ',') state = 7;
            else if (tokens[tok].id == '}') state = 6;
            else {
                errors.report(tokens[tok]);  break;
            }
        }
        if (tok + 1 == tokenB + tokenN && (state == 5 || state >= 7) && linei + 1 < lines.numEntries()) {
            // no more tokens. statement with {} can span multiple lines
            if (state == 5) {
                // after '='. expect next line to be '{'
                uint32_t tokNext = lines[linei+1].firstToken;
                if (tokens[tokNext].type != TOK_OPR || tokens[tokNext].id != '{') break; // anything else: break out of loop and get error message
            }
            // append next line
            lines[linei].type = LINE_DATADEF;
            linei++;
            tokenN += lines[linei].numTokens;
        }
 
    }
    // no more tokens
    if (state == 2 || state == 6) {
        // finish this definition
        lines[linei].type = LINE_DATADEF;
        state = 99; goto COMMA;
    }
    errors.report(tokens[tok-1].pos, tokens[tok-1].stringLength, ERR_UNFINISHED_VAR); 
}
 
// check if line is code or data
void CAssembler::determineLineType() {
    uint32_t tok;                           // current token
    uint32_t elements = 0;                  // detect type and constant tokens
 
    if (tokens[tokenB].type == TOK_OPT) {
        lines[linei].type = LINE_OPTIONS;  return;
    }
    // loop through tokens on this line
    for (tok = tokenB; tok < tokenB + tokenN; tok++) {
        if (tokens[tok].type == TOK_REG || tokens[tok].type == TOK_INS || tokens[tok].type == TOK_XPR || tokens[tok].type == TOK_HLL) {
            lines[linei].type = LINE_CODEDEF;  return;     // register or instruction found. must be code
        }
        if (tokens[tok].type == TOK_TYP) elements |= 1;
        if (tokens[tok].type == TOK_NUM || tokens[tok].type == TOK_FLT || tokens[tok].type == TOK_CHA || tokens[tok].type == TOK_STR) elements |= 2;
    }
    if (elements == 3)  lines[linei].type = LINE_DATADEF;
    else if (tokens[tokenB].type == TOK_ATT && tokens[tokenB].id == ATT_ALIGN) {  // align directive
        lines[linei].type = (sectionFlags & SHF_EXEC) ? LINE_CODEDEF : LINE_DATADEF;
    }
    else if (tokens[tokenB].type == TOK_EOF) lines[linei].type = 0;   // end of file
    else if (tokenN == 1 && tokens[tokenB].type == TOK_OPR && linei > 1) {
        // {} bracket. same type as previous line
        lines[linei].type = lines[linei-1].type;
    }
    else if (tokens[tokenB].type == TOK_OPR && tokens[tokenB].id == '%') {
        // metaprogramming code
        lines[linei].type = LINE_METADEF;
    }
    else if (linei > 1) {
        // undetermined. This may occur in for(;;) clause. Use same type as previous line
        lines[linei].type = lines[linei-1].type;
    }
    else {
        // error. cannot determine
        errors.report(tokens[tokenB]);
        lines[linei].type = LINE_ERROR;
    }
}
 
// interpret data or code alignment directive
void CAssembler::interpretAlign() {
    if (section) {
        uint32_t addr = (uint32_t)sectionHeaders[section].sh_size;
        SExpression exp1 = expression(tokenB+1, tokenN - 1, pass < 3 ? 0x10 : 0);
        if (exp1.tokens < tokenN - 1) {errors.report(tokens[tokenB+1+exp1.tokens]); return;}
        if ((exp1.etype & XPR_IMMEDIATE) != XPR_INT || (exp1.etype & (XPR_STRING | XPR_REG | XPR_OP | XPR_MEM | XPR_OPTION))) {
            errors.report(tokens[tokenB+1]);  return;
        }
        uint64_t alignm = exp1.value.u;
        if ((alignm & (alignm - 1)) || alignm > MAX_ALIGN) {errors.reportLine(ERR_ALIGNMENT);  return;}
        uint32_t log2ali = bitScanReverse(alignm);
        if (sectionHeaders[section].sh_align < log2ali) {
            sectionHeaders[section].sh_align = log2ali;  // make sure section alignment is not less
        }
        if (addr & ((uint32_t)alignm - 1)) { // needs to insert zeroes
            uint32_t addr2 = (addr + (uint32_t)alignm - 1) & -(int32_t)alignm;
            sectionHeaders[section].sh_size = addr2;           // update address
            if (pass >= 3) {
                dataBuffers[section].align((uint32_t)alignm);  // put zeroes in data buffer
            }
        }
    }
}
 
// Pass 3 does three things. 
// A. Handle metaprogramming directives
// B. Classify lines
// C. Identify symbol names, sections, labels, functions 
// These must be done in parallel because metaprogramming directives can refer to previously 
// defined symbols, and data/code definitions can involve metaprogramming variables and macros
 
void CAssembler::pass2() {
    ElfFWC_Sym2 sym;                  // symbol record
    zeroAllMembers(sym);              // reset symbol
    symbols.push(sym);                // symbol record 0 is empty
    symbolNameBuffer.put((char)0);    // put dummy zero to avoid zero offset at next string
    sectionFlags = 0;
    section = 0;
 
    // lines loop
    for (linei = 1; linei < lines.numEntries(); linei++) {
        lineError = 0;
        tokenB = lines[linei].firstToken;      // first token in line        
        tokenN = lines[linei].numTokens; // number of tokens in line
        if (tokenN == 0) continue;
        replaceKnownNames();                   // replace previously defined names by symbol references
        // check if line begins with '%'
        if (tokens[tokenB].type == TOK_OPR && tokens[tokenB].id == '%') {
            // metaprogramming code
            lines[linei].type = LINE_METADEF;
            interpretMetaDefinition();
            continue;
        }
        // classify other lines
        lines[linei].sectionType = sectionFlags;                    // line is section directive
        if (sectionFlags & ATT_EXEC) lines[linei].type = LINE_CODEDEF;
        else if (sectionFlags & ((ATT_READ | ATT_WRITE))) lines[linei].type = LINE_DATADEF;
 
        if (tokenN > 1) {
            // search for section, function and symbol definitions
            // lines with a single token cannot legally define a symbol name
            if ((tokens[tokenB].type == TOK_NAM || tokens[tokenB].type == TOK_SYM) && tokens[tokenB+1].type == TOK_DIR) {
                switch (tokens[tokenB + 1].id) {
                case DIR_SECTION:   // section starts here
                    interpretSectionDirective();
                    break;
                case DIR_FUNCTION:   // function starts here
                    interpretFunctionDirective();
                    break;
                case DIR_END:    // section or function end
                    interpretEndDirective(); 
                    break;
                default:
                    errors.report(tokens[tokenB + 1]);
                }
            }
            else if (tokens[tokenB].id == DIR_EXTERN) {
                // extern symbols
                interpretExternDirective();
            }
            else if (tokens[tokenB].id == DIR_PUBLIC) {
                // the interpretation of public symbol declarations is postponed to pass 4 after all 
                // symbols have been defined and got their final value
                lines[linei].type = LINE_PUBLICDEF;
            }
            else if (tokens[tokenB].type == TOK_NAM && tokens[tokenB+1].id == ':') {
                interpretLabel(tokenB);
                if (lines[linei].type == LINE_DATADEF) interpretVariableDefinition1();
            }
            else if (tokens[tokenB].type == TOK_TYP && (tokens[tokenB+1].type == TOK_NAM || tokens[tokenB+1].type == TOK_SYM)) {
                interpretVariableDefinition2();
            }
            else if (tokens[tokenB].type == TOK_ATT && tokens[tokenB].id == ATT_ALIGN) {  
                interpretAlign();
            }
            else if (tokens[tokenB].type == TOK_SYM && tokens[tokenB+1].id == ':' && pass == 2) {
                errors.report(tokens[tokenB].pos, tokens[tokenB].stringLength, ERR_SYMBOL_DEFINED);  // symbol already defined
            }
            else {
                determineLineType();  // check if code or data
                if (lines[linei].type == LINE_DATADEF) interpretVariableDefinition1();
            }
        }
        else {
            determineLineType();  // check if code or data (can only be code)
        }
    }
 
    // loop through lines again to replace names that are forward references to symbols defined during pass 2
    for (linei = 1; linei < lines.numEntries(); linei++) {
        tokenB = lines[linei].firstToken;      // first token in line        
        tokenN = lines[linei].numTokens;      // number of tokens in line
        replaceKnownNames();                   // replace previously defined names by symbol references
    }
}
 
 
// Show all symbols. For debugging only
void CAssembler::showSymbols() {
    uint32_t symi;
    ElfFWC_Sym2 sym;
    printf("\n\nSymbol:    name, section, addr, type, size, binding");
    for (symi = 1; symi < symbols.numEntries(); symi++) {
        sym = symbols[symi];
        printf("\n%3i: %10s, %7i, %4X", symi, symbolNameBuffer.buf() + sym.st_name,
            sym.st_section, (uint32_t)sym.st_value);
        if (sym.st_type == STT_CONSTANT || sym.st_type == STT_VARIABLE) {
            if (sym.st_other & STV_FLOAT) {    // floating point constant
                union { uint64_t i; double d; } val;
                val.i = sym.st_value;
                printf(" = %G", val.d);
            }
            else if (sym.st_other & STV_STRING) {   // string
                printf(" = %s", stringBuffer.getString((uint32_t)sym.st_value));
            }
            else {
                // print 64 bit integer constant
                printf(" = 0x");
                if (uint64_t(sym.st_value) >> 32) {
                    printf("%X%08X", uint32_t(sym.st_value >> 32), uint32_t(sym.st_value));
                }
                else {
                    printf("%X", uint32_t(sym.st_value));
                }
                // this method causes warnings:
                // printf(((sizeof(long int) > 4) ? " = 0x%lx" : " = 0x%llx"), sym.st_value); 
            }
        }
        else {
            printf(" %5X, %X*%X, %7X",  // other type
                sym.st_type, sym.st_unitsize, sym.st_unitnum, sym.st_bind);
        }
    }
}
 
// Show all tokens. For debugging only
void CAssembler::showTokens() {
    SKeyword const tokenNames[] = {        
        {"name",        TOK_NAM},   // unidentified name        
        {"direc",       TOK_DIR},   // section or function directive
        {"attrib",      TOK_ATT},   // section or function attribute
        {"label",       TOK_LAB},   // code label or function name
        {"datalb",      TOK_VAR},   // data label
        {"secnm",       TOK_SEC},   // section name
        {"type",        TOK_TYP},   // type name
        {"reg",         TOK_REG},   //  register name
        {"instr",       TOK_INS},   // instruction name 
        {"oper",        TOK_OPR},   // operator
        {"option",      TOK_OPT},   // operator
        {"num",         TOK_NUM},   // number
        {"float",       TOK_FLT},   // floating point number
        {"char",        TOK_CHA},   // character or string in single quotes ' '
        {"string",      TOK_STR},   // string in double quotes " "
        {"symbol",      TOK_SYM},   // symbol
        {"expression",  TOK_XPR},   // expression
        {"eof",         TOK_EOF},   // string in double quotes " "
        {"hll",         TOK_HLL}    // string in double quotes " "
                                    //   {"error", TOK_ERR}   // error. illegal character or unmatched quote
    };
 
    uint32_t line, tok, i;
    for (line = 1; line < lines.numEntries(); line++) {
        if (line < lines.numEntries() && lines[line].numTokens) {
            printf("\nline %2i type %X", lines[line].linenum, lines[line].type);
 
            for (tok = lines[line].firstToken; tok < lines[line].firstToken + lines[line].numTokens; tok++) {
                // find name for token type
                const char * nm = 0;
                for (i = 0; i < TableSize(tokenNames); i++) {
                    if (tokenNames[i].id == tokens[tok].type) nm = tokenNames[i].name;
                }
                if (nm) printf("\n%4X  %8s: ", tok, nm);                               // Token type
                else printf("type %4X", tokens[tok].type);
 
                switch (tokens[tok].type) {
                case TOK_DIR: case TOK_ATT: case TOK_TYP: case TOK_OPT: case TOK_HLL:
                    nm = 0;
                    for (i = 0; i < TableSize(keywordsList); i++) {
                        if (keywordsList[i].id == tokens[tok].id) nm = keywordsList[i].name;
                    }
                    if (nm) printf("%s", nm);
                    else printf("%4X %2i", tokens[tok].pos, tokens[tok].stringLength);
                    break;
                case TOK_OPR:
                    nm = 0;
                    for (i = 0; i < TableSize(operatorsList); i++) {
                        if (operatorsList[i].id == tokens[tok].id) nm = operatorsList[i].name;
                    }
                    if (nm) printf("%s", nm);
                    else printf("%4X %2i", tokens[tok].pos, tokens[tok].stringLength);
                    break;
                case TOK_REG: //registerNames
                    nm = 0;
                    for (i = 0; i < TableSize(registerNames); i++) {
                        if (registerNames[i].id == tokens[tok].id) nm = registerNames[i].name;
                    }
                    if (nm) printf("%s%i", nm, tokens[tok].id & 0xFF);
                    else printf("%4X %2i", tokens[tok].pos, tokens[tok].stringLength);
                    break;
                case TOK_NAM: case TOK_NUM: case TOK_FLT: case TOK_LAB: case TOK_VAR: case TOK_SEC:
                case TOK_CHA: case TOK_STR: case TOK_INS: case TOK_SYM:
                    for (i = 0; i < tokens[tok].stringLength; i++) {
                        printf("%c", buf()[tokens[tok].pos + i]);
                    }
                    printf("  id %X, value %X", tokens[tok].id, tokens[tok].value.w);
                    break;
                case TOK_XPR:
                default:
                    printf("0x%X 0x%X 0x%X %2i", tokens[tok].id, tokens[tok].value.w, tokens[tok].pos, tokens[tok].stringLength);
                    break;
                }
            }
        }
    }
} 
 
void CAssembler::initializeWordLists() {
    // Operators list
    operators.pushBig(operatorsList, sizeof(operatorsList));
    operators.sort();    
    // Keywords list
    keywords.pushBig(keywordsList,sizeof(keywordsList));
    keywords.sort();
    // Read instruction list from file
    CCSVFile instructionListFile; 
    instructionListFile.read(cmd.getFilename(cmd.instructionListFile), CMDL_FILE_SEARCH_PATH); // Filename of list of instructions
    instructionListFile.parse();                            // Read and interpret instruction list file
    instructionlist << instructionListFile.instructionlist; // Transfer instruction list to my own container
    instructionlistId.copy(instructionlist);                // copy instruction list
    instructionlistNm.copy(instructionlist);                // copy instruction list
                                                            // sort lists by different criteria, defined by the different operators:
    // operator < (SInstruction const & a, SInstruction const & b)
    // operator < (SInstruction3 const & a, SInstruction3 const & b)
    SInstruction3 nullInstruction;                          // empty record
    zeroAllMembers(nullInstruction);
    instructionlistId.push(nullInstruction);                // Empty record will go to position 0 to avoid an instruction with index 0
    instructionlistNm.sort();                               // Sort instructionlist by name
    instructionlistId.sort();                               // Sort instructionlistId by id
}
 

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.