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

Subversion Repositories forwardcom

[/] [forwardcom/] [bintools/] [assem5.cpp] - Rev 97

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

/****************************    assem5.cpp    ********************************
* Author:        Agner Fog
* Date created:  2017-09-19
* Last modified: 2021-05-21
* Version:       1.11
* Project:       Binary tools for ForwardCom instruction set
* Module:        assem.cpp
* Description:
* Module for assembling ForwardCom .as files. 
* This module contains functions for interpreting high level language constructs:
* functions, branches, and loops
*
* Copyright 2017-2021 GNU General Public License http://www.gnu.org/licenses
******************************************************************************/
#include "stdafx.h"
 
// Define high level block types
const int HL_SECTION     =  1;  // section
const int HL_FUNC        =  2;  // function
const int HL_IF          =  3;  // if branch
const int HL_ELSE        =  4;  // else branch
const int HL_SWITCH      =  5;  // switch-case branch
const int HL_FOR         =  6;  // for loop
const int HL_FOR_IN      =  7;  // vector loop. for (v1 in [r2-r3]) {}
const int HL_WHILE       =  8;  // while loop
const int HL_DO_WHILE    =  9;  // do-while loop
 
// invert condition code for branch instruction
void invertCondition(SCode & code) {
    code.instruction ^= II_JUMP_INVERT;  // invert condition code
    if ((code.dtype & TYP_FLOAT)
        && (code.instruction & 0xFF) == II_COMPARE
        && (code.instruction & 0x7F00) - 0x1000 < 0x2000) {
        // floating point compare instructions, except jump_ordered, must invert the unordered bit
        code.instruction ^= II_JUMP_UNORDERED; // inverse condition is unordered
    }
}
 
// if, else, switch, for, do, while statements
void CAssembler::interpretHighLevelStatement() {
    //uint32_t label = 0;
    if (tokenN > 2 && tokens[tokenB].type == TOK_SYM && tokens[tokenB+1].id == ':') {
        // line starts with a label. insert label
        // (this will prevent merging of jump instruction. merging is not allowed when there is a label between the two instructions)
        SCode codeL;
        zeroAllMembers(codeL);
        //codeL.label = tokens[tokenB].value.w; //?
        codeL.label = tokens[tokenB].id;
        codeL.section = section;
        codeBuffer.push(codeL);
        // interpret directive after label
        tokenB += 2;
        tokenN -= 2;
    }
    uint32_t tok = tokenB;
    if (tokenN > 1 && tokens[tok].type == TOK_TYP) {
        tok++;  // skip type keyword
        if (tok+1 < tokenB+tokenN && tokens[tok].type == TOK_OPR && tokens[tok].id == '+') tok++;  // skip '+' after type
    }
    // expect HLL keyword here. dispatch to the corresponding function
    switch (tokens[tok].id) {
    case HLL_IF:
        codeIf();  break;
    case HLL_SWITCH:
        codeSwitch();  break;
    case HLL_CASE:
        codeCase();  break;
    case HLL_FOR:
        codeFor();  break;
    case HLL_WHILE:
        codeWhile();  break;
    case HLL_DO:
        codeDo();  break;
    case HLL_BREAK: case HLL_CONTINUE:
        if (tok != tokenB) {
            errors.report(tokens[tok]);   // cannot have type token before break or continue
            break;
        }
        codeBreak();  break;
    case HLL_PUSH:                        // may be replaced by macro later
        codePush();  break;
    case HLL_POP:                         // may be replaced by macro later
        codePop();  break;
    default:
        errors.report(tokens[tok]);
    }
}
 
 
// finish {} block
void CAssembler::interpretEndBracket() {
    uint32_t n = hllBlocks.numEntries();
    if (n == 0) {
        errors.reportLine(ERR_BRACKET_END);  // unmatched end bracket
        return;
    }
    // dispatch depending on type of block
    switch (hllBlocks[n-1].blockType) {
    case HL_FUNC: // function
        break;
    case HL_IF: case HL_ELSE: // if branch
        codeIf2();
        break;
    case HL_FOR: // for loop
        codeFor2();
        break;
    case HL_FOR_IN: // vector loop. for (v1 in [r2-r3]) {}
        codeForIn2();
        break;
    case HL_WHILE:    // while loop
        codeWhile2();
        break;
    case HL_DO_WHILE: // do-while loop
        codeDo2();
        break;
    case HL_SWITCH: // switch-case branch
        break;
    default:
        errors.reportLine(ERR_BRACKET_END);  // should not occur
    }
}
 
 
// Interpret if statement in assembly code
void CAssembler::codeIf() {
    uint32_t state = 0;                // 0: start, 1: after type, 2: after if, 3: after (, 4: after (type, 
                                       // 5: after expression, 6: after ')', 7: after {
    uint32_t tok;                      // current token index
    SBlock block;                      // block descriptor to save
    zeroAllMembers(block);             // reset
    block.blockType = HL_IF;           // if block
    SToken token;                      // current token
    SExpression expr;                  // expression in ()
    SCode code;                        // instruction code
    zeroAllMembers(code);              // reset
 
    // interpret line by state machine looping through tokens
    for (tok = tokenB; tok < tokenB + tokenN; tok++) {
        if (lineError) break;
        token = tokens[tok];
 
        switch (state) {
        case 0:  // start. expect type or 'if'
            if (token.type == TOK_TYP) {
                code.dtype = dataType = token.id & 0xFF;
                state = 1;
            }
            else if (token.id == HLL_IF) state = 2;
            else errors.report(token);
            break;
        case 1:  // after type. expect '+' or 'if'
            if (token.type == TOK_OPR && token.id == '+') {
                code.dtype |= TYP_PLUS;
            }
            else if (token.id == HLL_IF) state = 2;
            else errors.report(token);
            break;
        case 2: // after if. expect '('
            if (token.type == TOK_OPR && token.id == '(') state = 3;
            else errors.report(token.pos, token.stringLength, ERR_EXPECT_PARENTHESIS);
            break;
        case 3: // after '('. expect type or logical expression
            if (token.type == TOK_TYP && !code.dtype) {
                code.dtype = dataType = token.id & 0xFF;
                state = 4;
                break;
            }
            EXPRESSION:
            expr = expression(tok, tokenB + tokenN-tok, (code.dtype & TYP_UNS) != 0);
            if (lineError) return; 
 
            // insert logical expression into block
            insertAll(code, expr);
            tok += expr.tokens - 1;
            state = 5;
            break;
        case 4: // after "if (type". expect '+' or expression
            if (token.type == TOK_OPR && token.id == '+') {
                code.dtype |= TYP_PLUS;
                break;
            }
            // not a '+'. expect expression
            goto EXPRESSION;
        case 5: // after expression. expect ')'
            if (token.type == TOK_OPR && token.id == ')') state = 6;
            else {
                errors.report(token);  return;
            }
            break;
        }
    }
    // should end at state 6 because '{' should be on next pseudo-line
    if (state != 6) errors.report(token);
    if (lineError) return;
 
    if (linei == lines.numEntries()-1) {  // no more lines
        errors.reportLine(ERR_UNFINISHED_INSTRUCTION);
        return;
    } 
 
    // get next line
    if (linei == lines.numEntries()-1) {    // no more lines
        errors.reportLine(ERR_UNFINISHED_INSTRUCTION);
        return;
    }
    linei++;
    tokenB = lines[linei].firstToken;       // first token in line        
    tokenN = lines[linei].numTokens;        // number of tokens in line
    lineError = false;
 
    // expect '{'
    if (tokens[tokenB].id != '{') {
        errors.reportLine(ERR_EXPECT_BRACKET);
        return;
    }
    // interpret the condition expression
    linei--;                                // make any error message apply to previous line
    interpretCondition(code);
    linei++;
    // make instruction code
    code.etype |= XPR_JUMPOS | XPR_SYM1;
    code.section = section;
 
    // check if {} contains a jump only
    uint32_t target2 = hasJump(linei+1);
    if (target2) {
        if (linei + 2 < lines.numEntries() && lines[linei+2].numTokens == 1) {
            tok = lines[linei+2].firstToken;
            if (tokens[tok].type == TOK_OPR && tokens[tok].id == '}') {
                // the {} block contains a jump and nothing else
                // make conditional jump to target2 instead
                code.sym5 = target2;
                linei += 2;                          // finished processing these two lines
                // check if it can be merged with previous instruction
                mergeJump(code);
                // finish code and fit it
                checkCode1(code);
                if (lineError) return;    
                fitCode(code);       // find an instruction variant that fits
                if (lineError) return;     
                codeBuffer.push(code);// save code structure
 
                // check if there is an 'else' after if(){}
                if (linei + 2 < lines.numEntries() && lines[linei+1].numTokens == 1 && lines[linei+2].numTokens == 1) {
                    tok = lines[linei+1].firstToken;
                    if (tokens[tok].type == TOK_HLL && tokens[tok].id == HLL_ELSE) {
                        tok = lines[linei+2].firstToken;
                        if (tokens[tok].type == TOK_OPR && tokens[tok].id == '{') {
                            // make the 'else' ignored
                            linei += 2;
                            // make block record with no label
                            block.blockNumber = ++iIf;
                            block.startBracket = tok;
                            block.jumpLabel = 0;
                            // store block in hllBlocks stack. will be retrieved at matching '}'
                            hllBlocks.push(block);
                        }
                    }
                }
                return;
            }
        }
    }
    invertCondition(code);  // invert condition. jump to else block if logical expression false
 
    if (code.instruction == (II_JUMP | II_JUMP_INVERT)) {
        // constant: don't jump
        code.instruction = 0;
    }
    // make block record with label name
    block.blockNumber = ++iIf;
    block.startBracket = tokenB;
    char name[32];
    sprintf(name, "@if_%u_a", iIf);
    uint32_t symi = makeLabelSymbol(name);
    block.jumpLabel = symbols[symi].st_name;
 
    // store block in hllBlocks stack. will be retrieved at matching '}'
    hllBlocks.push(block);
 
    // store jump instruction
    code.sym5 = block.jumpLabel;
 
    // check if it can be merged with previous instruction
    mergeJump(code);
 
    // finish code and fit it
    checkCode1(code);
    if (lineError) return;    
    fitCode(code);       // find an instruction variant that fits
    if (lineError) return;     
    codeBuffer.push(code);// save code structure
}
 
 
// Finish if statement at end bracket
void CAssembler::codeIf2() {
    SCode code;                             // code record for jump label
    zeroAllMembers(code);                   // reset
    SBlock block = hllBlocks.pop();         // pop the stack of {} blocks
    uint32_t labelA = block.jumpLabel;      // jump target label to place here
    // check if there is an 'else' following the if(){}
    if (block.blockType == HL_IF && linei+2 < lines.numEntries() && tokens[lines[linei+1].firstToken].id == HLL_ELSE) {
        // there is an else. get next line with the else
        linei++;
        if (lines[linei].numTokens > 1) errors.report(tokens[lines[linei].firstToken+1]);  // something other than '{' after else
        // check if there is a '{' following the 'else'
        linei++;
        uint32_t tokenB = lines[linei].firstToken;
        if (lines[linei].numTokens > 1 || tokens[tokenB].type != TOK_OPR || tokens[tokenB].id != '{') {
            errors.reportLine(ERR_EXPECT_BRACKET); // expecting '{'
            return;
        }
        // make block record for jump to label b
        block.blockType = HL_ELSE;           // if-else block
        block.startBracket = tokenB;
        // make label name
        char name[32];
        sprintf(name, "@if_%u_b", block.blockNumber);
        uint32_t symi = makeLabelSymbol(name);
        block.jumpLabel = symbols[symi].st_name;
        hllBlocks.push(block);     // store block in hllBlocks stack. will be retrieved at matching '}'
 
        // make jump instruction
        code.section = section;
        code.instruction = II_JUMP;
        code.etype = XPR_JUMPOS | XPR_SYM1;
        code.sym5 = block.jumpLabel;
 
        // check if it can be merged with previous instruction
        mergeJump(code);
 
        // finish code and save it
        checkCode1(code);
        if (lineError) return;    
        fitCode(code);       // find an instruction variant that fits
        if (lineError) return;     
        codeBuffer.push(code);// save code structure
    }
    // make target label here
    if (labelA) {
        zeroAllMembers(code);
        code.section = section;
        code.label = labelA;       // jump target label
        codeBuffer.push(code);    // save code structure
    }
}
 
 
// Interpret while loop in assembly code
void CAssembler::codeWhile() {
    uint32_t state = 0;                // 0: start, 1: after type, 2: after while, 3: after (, 4: after (type, 
                                       // 5: after expression, 6: after ')', 7: after {
    uint32_t tok;                      // current token index
    SBlock block;                      // block descriptor to save
    zeroAllMembers(block);             // reset
    SToken token;                      // current token
    SExpression expr;                  // expression in ()
    SCode code;                        // instruction code
    zeroAllMembers(code);              // reset
 
    // interpret line by state machine looping through tokens (same as for codeIf)
    for (tok = tokenB; tok < tokenB + tokenN; tok++) {
        if (lineError) break;
        token = tokens[tok];
 
        switch (state) {
        case 0:  // start. expect type or 'while'
            if (token.type == TOK_TYP) {
                code.dtype = dataType = token.id & 0xFF;
                state = 1;
            }
            else if (token.id == HLL_WHILE) state = 2;
            else errors.report(token);
            break;
        case 1:  // after type. expect '+' or 'while'
            if (token.type == TOK_OPR && token.id == '+') {
                code.dtype |= TYP_PLUS;
            }
            else if (token.id == HLL_WHILE) state = 2;
            else errors.report(token);
            break;
        case 2: // after if. expect '('
            if (token.type == TOK_OPR && token.id == '(') state = 3;
            else errors.report(token.pos, token.stringLength, ERR_EXPECT_PARENTHESIS);
            break;
        case 3: // after '('. expect type or logical expression
            if (token.type == TOK_TYP && !code.dtype) {
                code.dtype = dataType = token.id & 0xFF;
                state = 4;
                break;
            }
        EXPRESSION:
            expr = expression(tok, tokenB + tokenN-tok, (code.dtype & TYP_UNS) != 0);
            if (lineError) return; 
 
            // insert logical expression into block
            insertAll(code, expr);
            tok += expr.tokens - 1;
            state = 5;
            break;
        case 4: // after "while (type". expect '+' or expression
            if (token.type == TOK_OPR && token.id == '+') {
                code.dtype |= TYP_PLUS;
                break;
            }
            // not a '+'. expect expression
            goto EXPRESSION;
        case 5: // after expression. expect ')'
            if (token.type == TOK_OPR && token.id == ')') state = 6;
            else {
                errors.report(token);  return;
            }
            break;
        }
    }
    // should end at state 6 because '{' should be on next pseudo-line
    if (state != 6) errors.report(token);
    if (lineError) return;
 
    if (linei == lines.numEntries()-1) {  // no more lines
        errors.reportLine(ERR_UNFINISHED_INSTRUCTION);
        return;
    } 
 
    // get next line
    // get next line
    if (linei == lines.numEntries()-1) {    // no more lines
        errors.reportLine(ERR_UNFINISHED_INSTRUCTION);
        return;
    }
    linei++;
    tokenB = lines[linei].firstToken;       // first token in line        
    tokenN = lines[linei].numTokens;        // number of tokens in line
    lineError = false;
 
    // expect '{'
    if (tokens[tokenB].id != '{') {
        errors.reportLine(ERR_EXPECT_BRACKET);
        return;
    }
 
    // interpret the condition expression
    linei--;                                // make any error message apply to previous line
    interpretCondition(code);
    linei++;
 
    // make instruction code
    code.etype |= XPR_JUMPOS | XPR_SYM1;
    code.section = section;
 
    // make block record with label names
    block.blockType = HL_WHILE;        // while-block
    block.blockNumber = ++iLoop;
    block.startBracket = tokenB;
    char name[32];
    sprintf(name, "@while_%u_a", iLoop);
    uint32_t symi1 = makeLabelSymbol(name);
    block.jumpLabel = symbols[symi1].st_name;  // label to jump back to
    sprintf(name, "@while_%u_b", iLoop);
    uint32_t symi2 = makeLabelSymbol(name);
    block.breakLabel = symbols[symi2].st_name;  // label after loop. used if condition is false first time and for break statements
    block.continueLabel = 0xFFFFFFFF;          // this label will only be made if there is a continue statement
 
    // make code to check condition before first iteration
    SCode code1 = code;
    invertCondition(code1); // invert condition to jump if false
 
    if (code1.instruction == (II_JUMP | II_JUMP_INVERT)) {
        // constant: don't jump
        code1.instruction = 0;
    } 
    code1.sym5 = block.breakLabel;
    // check if it can be merged with previous instruction
    mergeJump(code1);
    // finish code and store it
    checkCode1(code1);
    if (lineError) return;    
    fitCode(code1);       // find an instruction variant that fits
    if (lineError) return;     
    codeBuffer.push(code1);// save code structure
 
    // make loop label
    zeroAllMembers(code1);
    code1.label = block.jumpLabel;
    code1.section = section;
    codeBuffer.push(code1);// save code structure
 
    // make instruction to place at end of loop
    code.sym5 = block.jumpLabel;
    checkCode1(code);
    // store in codeBuffer2 for later insertion at the end of the loop
    block.codeBuffer2index = codeBuffer2.push(code);
    block.codeBuffer2num = 1;
 
    // store block in hllBlocks stack. will be retrieved at matching '}'
    hllBlocks.push(block);
}
 
// Finish while-loop at end bracket
void CAssembler::codeWhile2() {
    SCode code;                            // code record for jump back
    SBlock block = hllBlocks.pop();        // pop the stack of {} blocks
    if (block.continueLabel != 0xFFFFFFFF) {
        // place label here as jump target for continue statements
        zeroAllMembers(code);
        code.label = block.continueLabel;
        code.section = section;
        codeBuffer.push(code);
    }
 
    uint32_t codebuf2num = codeBuffer2.numEntries();
    if (block.codeBuffer2num && block.codeBuffer2index < codebuf2num) {
        // retrieve jumpback instruction
        code = codeBuffer2[block.codeBuffer2index];
 
        if (code.instruction == (II_JUMP | II_JUMP_INVERT)) {
            // constant: don't jump
            code.instruction = 0;
        } 
        // check if it can be merged with previous instruction
        mergeJump(code);
        // finish code and store it
        checkCode1(code);
        if (lineError) return;
        fitCode(code);       // find an instruction variant that fits
        if (lineError) return;
        codeBuffer.push(code);  // save code
        // remove from temporary buffer
        if (block.codeBuffer2index + 1 == codebuf2num) codeBuffer2.pop();
        // place label for breaking out
        zeroAllMembers(code);
        code.label = block.breakLabel;
        code.section = section;
        codeBuffer.push(code);  // save label
        return;
    }
}
 
// Interpret do-while loop in assembly code
void CAssembler::codeDo() {
    SBlock block;                          // block record
    SCode code;                            // code record for label
    zeroAllMembers(block);
    zeroAllMembers(code);
 
    // make block record with label names
    block.blockType = HL_DO_WHILE;        // do-while-block
    block.blockNumber = ++iLoop;
    char name[32];
    sprintf(name, "@do_%u_a", iLoop);
    uint32_t symi1 = makeLabelSymbol(name);
    block.jumpLabel = symbols[symi1].st_name;  // label to jump back to
    block.breakLabel = 0xFFFFFFFF;             // this label will only be made if there is a break statement
    block.continueLabel = 0xFFFFFFFF;          // this label will only be made if there is a continue statement
    // make loop label
    code.label = block.jumpLabel;
    code.section = section;
    codeBuffer.push(code);                     // save label
 
    // get next line with '{'
    if (linei == lines.numEntries()-1) {    // no more lines
        errors.reportLine(ERR_UNFINISHED_INSTRUCTION);
        return;
    }
    linei++;
    tokenB = lines[linei].firstToken;       // first token in line        
    tokenN = lines[linei].numTokens;        // number of tokens in line
    lineError = false; 
    // expect '{'
    if (tokens[tokenB].id != '{') {
        errors.report(tokens[tokenB]);
        return;
    }
    block.startBracket = tokenB;
    hllBlocks.push(block);                     // store block in hllBlocks stack. will be retrieved at matching '}'
}
 
// Finish do-while loop at end bracket
void CAssembler::codeDo2() {
    SCode code;                            // code record 
 
    SBlock block = hllBlocks.pop();        // pop the stack of {} blocks
    if (block.continueLabel != 0xFFFFFFFF) {
        // place label here as jump target for continue statements
        zeroAllMembers(code);
        code.label = block.continueLabel;
        code.section = section;
        codeBuffer.push(code);
    }
    // find 'while' keyword in next pseudo-line after '}'
    if (linei+1 >= lines.numEntries()) {                  // no more lines
        errors.reportLine(ERR_WHILE_EXPECTED); return;
    }
    linei++;
    tokenB = lines[linei].firstToken;       // first token in line        
    tokenN = lines[linei].numTokens;        // number of tokens in line
    lineError = false;
 
    // expect "while (expression)"
    uint32_t state = 0;                // 0: start, 1: after type, 2: after 'while', 3: after (, 4: after (type, 
                                       // 5: after expression, 6: after ')'
    uint32_t tok;                      // current token index
    SToken token;                      // current token
    SExpression expr;                  // expression in ()
    zeroAllMembers(code);              // reset code
 
    // interpret line by state machine looping through tokens
    for (tok = tokenB; tok < tokenB + tokenN; tok++) {
        if (lineError) return;
        token = tokens[tok];
 
        switch (state) {
        case 0:  // start. expect type or 'while'
            if (token.type == TOK_TYP) {
                code.dtype = dataType = token.id & 0xFF;
                state = 1;
            }
            else if (token.id == HLL_WHILE) state = 2;
            else errors.reportLine(ERR_WHILE_EXPECTED);
            break;
        case 1:  // after type. expect '+' or 'while'
            if (token.type == TOK_OPR && token.id == '+') {
                code.dtype |= TYP_PLUS;
            }
            else if (token.id == HLL_WHILE) state = 2;
            else errors.report(token);
            break;
        case 2: // after 'while'. expect '('
            if (token.type == TOK_OPR && token.id == '(') state = 3;
            else errors.report(token.pos, token.stringLength, ERR_EXPECT_PARENTHESIS);
            break;
        case 3: // after '('. expect type or logical expression
            if (token.type == TOK_TYP && !code.dtype) {
                code.dtype = dataType = token.id & 0xFF;
                state = 4;
                break;
            }
        EXPRESSION:
            expr = expression(tok, tokenB + tokenN - tok, (code.dtype & TYP_UNS) != 0);
            if (lineError) return;
 
            // insert logical expression into block
            insertAll(code, expr);
            tok += expr.tokens - 1;
            state = 5;
            break;
        case 4: // after "while (type". expect '+' or expression
            if (token.type == TOK_OPR && token.id == '+') {
                code.dtype |= TYP_PLUS;
                break;
            }
            // not a '+'. expect expression
            goto EXPRESSION;
        case 5: // after expression. expect ')'
            if (token.type == TOK_OPR && token.id == ')') state = 6;
            else {
                errors.report(token);  return;
            }
            break;
        }
    }
    // should end at state 6
    if (state != 6) errors.report(token);
    if (lineError) return;
 
    // make instruction with condition
    interpretCondition(code);
    code.etype |= XPR_JUMPOS | XPR_SYM1;
    code.section = section;
    code.sym5 = block.jumpLabel;
 
    if (code.instruction == (II_JUMP | II_JUMP_INVERT)) {
        // constant: don't jump
        code.instruction = 0;
    } 
    // check if it can be merged with previous instruction
    mergeJump(code);
    // finish code and fit it
    checkCode1(code);
    if (lineError) return;    
    fitCode(code);       // find an instruction variant that fits
    if (lineError) return;     
    codeBuffer.push(code);// save code structure
 
    if (block.breakLabel != 0xFFFFFFFF) {
        // place label here as jump target for break statements
        zeroAllMembers(code);
        code.label = block.breakLabel;
        code.section = section;
        codeBuffer.push(code);
    }
}
 
// Interpret for-loop in assembly code
void CAssembler::codeFor() {
    uint32_t tok;                                 // token number
 
    // search for 'in' keyword
    for (tok = tokenB; tok < tokenB + tokenN; tok++) {
        if (tokens[tok].type == TOK_HLL && tokens[tok].id == HLL_IN) {
            // this is a for-in vector loop
            codeForIn();
            return;
        }
    }
 
    // this is a traditional for(;;) loop
    uint32_t state = 0;                // state while interpreting line
                                       // 0: start, 1: after type, 2: after 'for', 3: after (, 4: after (type, 
    SBlock block;                      // block descriptor to save
    zeroAllMembers(block);             // reset
    block.blockType = HL_FOR;          // 'for' block
    block.breakLabel = block.jumpLabel = block.continueLabel = 0xFFFFFFFF;
    SToken token;                      // current token
    SToken typeToken;                  // token defining type
    // uint32_t type = 0;                 // operand type for all three expressions in for(;;)
    dataType = 0;                      // operand type for all three expressions in for(;;)
    uint32_t symi = 0;                 // symbol index
    char name[48];                     // symbol name
    int conditionFirst = 0;            // evaluation of the condition before first iteration:
                                       // 0: condition must be checked before first iteration
                                       // 2: condition is false before first iteration. zero iterations
                                       // 3: condition is true before first iteration. no initial check needed
 
    // interpret line by state machine looping through tokens
    for (tok = tokenB; tok < tokenB + tokenN; tok++) {
        if (lineError) break;
        token = tokens[tok];
 
        if (state == 0) { // start. expect type or 'for'
            if (token.type == TOK_TYP) {
                dataType = token.id & 0xFF;
                typeToken = token;
                state = 1;
            }
            else if (token.id == HLL_FOR) state = 2;
            else errors.report(token);
        }
        else if (state == 1) { // after type. expect '+' or 'for'
            if (token.type == TOK_OPR && token.id == '+') {
                dataType |= TYP_PLUS;
            }
            else if (token.id == HLL_FOR) state = 2;
            else errors.report(token);
        }
        else if (state == 2) { // after 'for'. expect '('
            if (token.type == TOK_OPR && token.id == '(') state = 3;
            else errors.report(token.pos, token.stringLength, ERR_EXPECT_PARENTHESIS);
        }
        else if (state == 3) { // after '('. expect type or initialization
            if (token.type == TOK_TYP && !dataType) {
                dataType = token.id & 0xFF;
                typeToken = token;
                tok++;
                if (tok < tokenB + tokenN && tokens[tok].type == TOK_OPR && tokens[tok].id == '+') {
                    // '+' after type
                    dataType |= TYP_PLUS;
                    tok++;
                }
            }
            state = 4;
            break;  // end tok loop here            
        }
    }
    if (state != 4) {
        errors.report(token);
        return;
    }
    if (lineError) return;
 
    // check type
    if (dataType == 0) {
        errors.reportLine(ERR_TYPE_MISSING);  return;
    }
    // extend type to int32 if allowed. this allows various optimizations
    // (unsigned types not allowed, even if start and end values are known to be positive, because loop counter may become negative inside loop in rare cases)
    if ((dataType & TYP_PLUS) && ((dataType & 0xFF) < (TYP_INT32 & 0xFF))) dataType = TYP_INT32;
 
    // remake token sequence for generating initial instruction
    uint32_t tokensRestorePoint = tokens.numEntries();
    typeToken.id = dataType;
    tokens.push(typeToken);
    uint32_t tokenFirst = tok;
    if (tokens[tokenFirst].type == TOK_TYP) tokenFirst++; // skip type token. it has already been inserted
    for (; tok < tokenB + tokenN; tok++) {  // insert remaining tokens
        token = tokens[tok];
        if (token.type == TOK_OPR && token.id == ';') break;
        tokens.push(tokens[tok]);
    }
    // make an instruction out of this sequence
    tokenB = tokensRestorePoint;
    tokenN = tokens.numEntries() - tokensRestorePoint;
    uint32_t codePoint = codeBuffer.numEntries();
    SCode initializationCode;
    zeroAllMembers(initializationCode);
    if (tokenN > 1) {   // skip if there is no code
        interpretCodeLine();
        if (codeBuffer.numEntries() == codePoint + 1) {
            // remember initialization code
            initializationCode = codeBuffer[codePoint];
        }
    }
    // remove temporary token sequence
    tokens.setNum(tokensRestorePoint);
    if (lineError) return;
 
    // get next line containing loop condition
    SCode conditionCode;
    zeroAllMembers(conditionCode);
    conditionCode.section = section;
    if (linei+2 >= lines.numEntries()) {  // no more lines
        errors.reportLine(ERR_UNFINISHED_INSTRUCTION);
        return;
    }
    linei++;
    tokenB = lines[linei].firstToken;       // first token in line        
    tokenN = lines[linei].numTokens;        // number of tokens in line
    if (tokenN == 1 && tokens[tokenB].type == TOK_OPR && tokens[tokenB].id == ';') {
        // no condition specified. infinite loop
        conditionFirst = 3;
        conditionCode.instruction = II_JUMP;
        conditionCode.etype = XPR_JUMPOS;
    }
    else {
        SExpression expr = expression(tokenB, tokenN, (dataType & TYP_UNS) != 0);
        if (lineError) return;
        insertAll(conditionCode, expr);  // insert logical expression into block
        conditionCode.dtype = dataType;
        interpretCondition(conditionCode);  // convert expression to conditional jump
        if (conditionCode.etype == XPR_INT) { // always true or always false
            conditionFirst = 2 + (conditionCode.value.w & 1);
            conditionCode.instruction = II_JUMP;
            conditionCode.etype = XPR_JUMPOS;
            conditionCode.value.i = 0;
            conditionCode.dtype = 0;
        }
        else {
            conditionCode.etype |= XPR_JUMPOS | XPR_SYM1;
            conditionCode.section = section;
            tok = tokenB + expr.tokens;       // expect ';' after expression
            if (tokens[tok].type != TOK_OPR || tokens[tok].id != ';') {
                errors.report(tokens[tok]);
            }
            //uint32_t counterRegister = 0;
            uint64_t counterStart = 0;
            // are start and end values known constants?
            if (initializationCode.instruction == II_MOVE && (initializationCode.etype & XPR_INT) && initializationCode.dest
                && !(initializationCode.etype & (XPR_REG1 | XPR_MEM | XPR_OPTION))) {
                //counterRegister = initializationCode.dest;
                counterStart = initializationCode.value.i;
                if ((expr.etype & XPR_INT) && (expr.etype & XPR_REG1) && !(expr.etype & (XPR_REG2 | XPR_MEM | XPR_OPTION))) {
                    // start and end values are integer constants
                    if ((expr.instruction & 0xFF) == II_COMPARE) {
                        // compare instruction. condition is in option bits
                        switch ((expr.optionbits >> 1) & 3) {
                        case 0:  // ==
                            conditionFirst = 2 + (counterStart == expr.value.u);  break;
                        case 1:  // <
                            if (dataType & TYP_UNS) conditionFirst = 2 + (counterStart < expr.value.u);
                            else conditionFirst = 2 + ((int64_t)counterStart < expr.value.i);
                            break;
                        case 2:  // >
                            if (dataType & TYP_UNS) conditionFirst = 2 + (counterStart > expr.value.u);
                            else conditionFirst = 2 + ((int64_t)counterStart > expr.value.i);
                            break;
                        }
                        if (expr.optionbits & 1) conditionFirst ^= 1; // invert if bit 0    
                    }
                    else if ((expr.instruction & 0xFF) == II_AND) {
                        // bit test. if (r1 & 1 << n)
                        conditionFirst = 2 + ((counterStart & ((uint64_t)1 << expr.value.u)) != 0);
                    }
                }
            }
        }
    }
 
    // make block record with label name
    block.blockNumber = ++iLoop;
 
    if (conditionFirst == 0) {
        // condition must be checked before first iteration
        invertCondition(conditionCode);          // invert condition
        sprintf(name, "@for_%u_b", iLoop);
        symi = makeLabelSymbol(name);
        block.breakLabel = symbols[symi].st_name;
        conditionCode.sym5 = block.breakLabel;
        // check if it can be merged with previous instruction
        mergeJump(conditionCode);
        checkCode1(conditionCode);               // finish code and fit it
        if (lineError) return;    
        fitCode(conditionCode);                  // find an instruction variant that fits
        if (lineError) return;     
        codeBuffer.push(conditionCode);          // save code structure
        invertCondition(conditionCode);          // invert condition back again
    }
    else if (conditionFirst == 2) {
        // condition is known to be false. loop goes zero times
        SCode jumpAlways;
        zeroAllMembers(jumpAlways);
        jumpAlways.instruction = II_JUMP;        // jump past loop
        jumpAlways.section = section;
        jumpAlways.etype = XPR_JUMPOS;
        sprintf(name, "@for_%u_goes_zero_times", iLoop);
        symi = makeLabelSymbol(name);
        block.breakLabel = symbols[symi].st_name;
        jumpAlways.sym5 = block.breakLabel;
        // check if it can be merged with previous instruction
        mergeJump(jumpAlways);
        checkCode1(jumpAlways);                  // finish code and fit it
        if (lineError) return;    
        fitCode(jumpAlways);                     // find an instruction variant that fits
        if (lineError) return;     
        codeBuffer.push(jumpAlways);             // save code structure
    }
    // make label for loop back
    if (conditionCode.instruction != II_JUMP) {    
        sprintf(name, "@for_%u_a", iLoop);
    }
    else {
        sprintf(name, "@infinite_loop_%u_a", iLoop);
    }
    symi = makeLabelSymbol(name);
    block.jumpLabel = symbols[symi].st_name;
    conditionCode.sym5 = block.jumpLabel;
    SCode codeLabel;
    zeroAllMembers(codeLabel);
    codeLabel.label = block.jumpLabel;
    codeLabel.section = section;    
    codeBuffer.push(codeLabel);
 
    // get next line containing increment
    linei++;
    tokenB = lines[linei].firstToken;            // first token in line        
    tokenN = lines[linei].numTokens;             // number of tokens in line
    // line must end with ')'
    if (tokenN < 1) {
        errors.reportLine(ERR_UNFINISHED_INSTRUCTION);
        return;
    }
    if (tokens[tokenB+tokenN-1].type != TOK_OPR || tokens[tokenB+tokenN-1].id != ')') {
        errors.report(tokens[tokenB+tokenN-1]);  // expecting ')'
        return;
    }
 
    // make instruction for loop counter increment
    tokens.push(typeToken);
    for (tok = tokenB; tok < tokenB + tokenN - 1; tok++) { // insert remaining tokens
        tokens.push(tokens[tok]);
    }
    // make an instruction out of this sequence
    tokenB = tokensRestorePoint;
    tokenN = tokens.numEntries() - tokensRestorePoint;
    SCode incrementCode;
    zeroAllMembers(incrementCode);
    codePoint = codeBuffer.numEntries();
    if (tokenN > 1) {
        interpretCodeLine();
        if (codeBuffer.numEntries() == codePoint + 1) {
            incrementCode = codeBuffer[codePoint];        
            incrementCode.section = section;
        }
    }
    // remove temporary token sequence
    tokens.setNum(tokensRestorePoint);
    // remove temporary incrementation code. it has to be inserted after the loop
    codeBuffer.setNum(codePoint);
    if (lineError) return;
 
    // save instructions in block
    block.codeBuffer2index = codeBuffer2.push(incrementCode);
    codeBuffer2.push(conditionCode);
    block.codeBuffer2num = 2;
 
    // get next line containing '{'
    linei++;
    tokenB = lines[linei].firstToken;            // first token in line        
    tokenN = lines[linei].numTokens;             // number of tokens in line
    // line must contain '{'
    if (tokenN != 1 || tokens[tokenB].type != TOK_OPR || tokens[tokenB].id != '{') {
        errors.reportLine(ERR_EXPECT_BRACKET);
        return;
    }
    block.startBracket = tokenB;
 
    // save block to be recalled at end bracket
    hllBlocks.push(block);
 
}
 
// Finish for-loop at end bracket
void CAssembler::codeFor2() {
    SCode incrementCode;                         // code record for incrementing loop counter
    SCode conditionCode;                         // code record for conditional jump back
    SCode labelCode;                             // code record for label
    SBlock block = hllBlocks.pop();              // pop the stack of {} blocks
    if (block.continueLabel != 0xFFFFFFFF) {
        // place label here as jump target for continue statements
        zeroAllMembers(labelCode);
        labelCode.label = block.continueLabel;
        labelCode.section = section;
        codeBuffer.push(labelCode);
    }
 
    uint32_t codebuf2num = codeBuffer2.numEntries();
    if (block.codeBuffer2num == 2 && block.codeBuffer2index < codebuf2num) {
        // retrieve prepared instruction
        incrementCode = codeBuffer2[block.codeBuffer2index];
        conditionCode = codeBuffer2[block.codeBuffer2index+1];
 
        // finish increment code and store it
        if (incrementCode.instruction) {
            checkCode1(incrementCode);
            if (lineError) return;
            fitCode(incrementCode);              // find an instruction variant that fits
            if (lineError) return;
            codeBuffer.push(incrementCode);      // save code
        }
 
        // finish condition code and store it
        // check if it can be merged with previous instruction
        mergeJump(conditionCode);
        checkCode1(conditionCode);
        if (lineError) return;
        fitCode(conditionCode);                  // find an instruction variant that fits
        if (lineError) return;
        codeBuffer.push(conditionCode);          // save code
 
        // remove from temporary buffer
        if (block.codeBuffer2index + 2 == codebuf2num) {
            codeBuffer2.pop(); codeBuffer2.pop();
        }
        // place label for breaking out
        if (block.breakLabel != 0xFFFFFFFF) {
            zeroAllMembers(labelCode);
            labelCode.label = block.breakLabel;
            labelCode.section = section;
            codeBuffer.push(labelCode);          // save label
        }
    }
}
 
// Interpret for-in vector loop in assembly code
// for (float v1 in [r1-r2]) {}
void CAssembler::codeForIn() {
    uint32_t state = 0;                // state while interpreting line
                                       // 0: start, 1: after type, 2: after 'for', 3: after (, 4: after (type,
                                       // 5: after vector register, 6: after 'in', 7: after '['
                                       // 8: after base register, 9: after '-', 10: after index register
                                       // 11: after ']', 12: after ')'
    SBlock block;                      // block descriptor to save
    zeroAllMembers(block);             // reset
    block.blockType = HL_FOR_IN;       // 'for-in' block
    block.breakLabel = block.jumpLabel = block.continueLabel = 0xFFFFFFFF;
    block.blockNumber = ++iLoop;       // number to use in labels
    //uint32_t baseReg;                // base register
    uint32_t indexReg = 0;             // index register
    uint32_t tok;                      // token number
    SToken token;                      // current token
    uint32_t type = 0;                 // vector element type
    uint32_t symi;                     // symbol index
    char name[32];                     // symbol name
 
    // interpret line by state machine looping through tokens
    for (tok = tokenB; tok < tokenB + tokenN; tok++) {
        if (lineError) break;
        token = tokens[tok];
 
        switch (state) {
        case 0:  // start. expect type or 'for'
            if (token.type == TOK_TYP) {
                type = token.id & 0xFF;
                state = 1;
            }
            else if (token.type == TOK_HLL && token.id == HLL_FOR) {
                state = 2;
            }
            else errors.report(token);
            break;
        case 1:  // after type. expect 'for' ('+' not allowed after type)
            if (token.type == TOK_HLL && token.id == HLL_FOR) {
                state = 2;
            }
            else errors.report(token);
            break;
        case 2:  // after 'for'. expect '('
            if (token.type == TOK_OPR && token.id == '(') {
                state = 3;
            }
            else errors.report(token);
            break;
        case 3:  // after '('. expect type or vector register
            if (token.type == TOK_TYP && !type) {
                type = token.id & 0xFF;
                state = 4;
            }
            else if (token.type == TOK_REG) {
                // must be vector register
                if (!(token.id & REG_V)) errors.report(token.pos, token.stringLength, ERR_WRONG_REG_TYPE);
                state = 5;
            }
            else errors.report(token);
            break;
        case 4:  // after type. expect vector register         
            if (token.type == TOK_REG) {
                // must be vector register
                if (!(token.id & REG_V)) errors.report(token.pos, token.stringLength, ERR_WRONG_REG_TYPE);
                state = 5;
            }
            else errors.report(token);
            break;
        case 5:  // after vector register. expect 'in'
            if (token.type == TOK_HLL && token.id == HLL_IN) {
                state = 6;
            }
            else errors.report(token);
            break;
        case 6:  // after 'in'. expect '['
            if (token.type == TOK_OPR && token.id == '[') {
                state = 7;
            }
            else errors.report(token);
            break;
        case 7:  // after '['. expect base register
            if (token.type == TOK_REG) {
                // must be general purpose register
                if (!(token.id & REG_R)) errors.report(token.pos, token.stringLength, ERR_WRONG_REG_TYPE);
                //baseReg = token.id;
                state = 8;
            }
            else errors.report(token);
            break;
        case 8:  // after base register. expect '-'
            if (token.type == TOK_OPR && token.id == '-') {
                state = 9;
            }
            else errors.report(token);
            break;
        case 9:  // after '-'. expect index register
            if (token.type == TOK_REG) {
                // must be general purpose register, except r31
                if (!(token.id & REG_R) || token.id == (REG_R|31)) errors.report(token.pos, token.stringLength, ERR_WRONG_REG_TYPE);
                indexReg = token.id;
                state = 10;
            }
            else errors.report(token);
            break;
        case 10:  // after index register. expect ']'
            if (token.type == TOK_OPR && token.id == ']') {
                state = 11;
            }
            else errors.report(token);
            break;
        case 11:  // after ']'. expect ')'
            if (token.type == TOK_OPR && token.id == ')') {
                state = 12;
            }
            else errors.report(token);
            break;
        default:
            errors.report(token);
        }
    }
    // get next line and expect '{'
    if (linei == lines.numEntries()-1) {    // no more lines
        errors.reportLine(ERR_UNFINISHED_INSTRUCTION);
        return;
    }
    linei++;
    tokenB = lines[linei].firstToken;       // first token in line        
    tokenN = lines[linei].numTokens;        // number of tokens in line
    lineError = false;
 
    // expect '{'
    if (tokens[tokenB].id != '{') {
        errors.reportLine(ERR_EXPECT_BRACKET);
        return;
    }
    block.startBracket = tokenB;
 
    // look at preceding instruction to see if value of index register is known to be positive
    bool startCheckNeeded = true;
    SCode previousInstruction;
    if (codeBuffer.numEntries()) {
        previousInstruction = codeBuffer[codeBuffer.numEntries()-1];  // recall previous instruction
        if (previousInstruction.section == section && previousInstruction.instruction == II_MOVE
            && (previousInstruction.etype & XPR_INT) && previousInstruction.dest == indexReg
            && !(previousInstruction.etype & (XPR_REG1 | XPR_MEM | XPR_OPTION))
            && previousInstruction.value.i > 0) {
            startCheckNeeded = false;
        }
    }
    if (startCheckNeeded) {
        // make label name for break
        sprintf(name, "@for_%u_b", iLoop);
        symi = makeLabelSymbol(name);
        block.breakLabel = symbols[symi].st_name; // label to jump back to
        // make conditional jump if index not positive
        SCode startCheck;
        zeroAllMembers(startCheck);
        startCheck.section = section;
        startCheck.instruction = II_COMPARE | II_JUMP_POSITIVE | II_JUMP_INVERT;
        startCheck.reg1 = indexReg;
        startCheck.sym5 = block.breakLabel;
        startCheck.etype = XPR_INT | XPR_REG | XPR_REG1 | XPR_JUMPOS;
        startCheck.line = linei;
        startCheck.dtype = TYP_INT64;        
        mergeJump(startCheck);                   // check if it can be merged with previous instruction
        checkCode1(startCheck);
        fitCode(startCheck);                     // find an instruction variant that fits
        if (lineError) return;     
        codeBuffer.push(startCheck);             // save instruction
    }
    // make label name for loop
    sprintf(name, "@for_%u_a", iLoop);
    symi = makeLabelSymbol(name);
    block.jumpLabel = symbols[symi].st_name;     // label to jump back to
    SCode labelCode;
    zeroAllMembers(labelCode);
    labelCode.section = section;
    labelCode.label = block.jumpLabel;
    codeBuffer.push(labelCode);                  // save label
 
    // save index registr and type in block
    block.codeBuffer2num = indexReg;
    block.codeBuffer2index = type;
 
    // save block to be recalled at '}'
    hllBlocks.push(block);
}
 
// Finish for-in vector loop in assembly code
void CAssembler::codeForIn2() {
    SCode code;                                  // code record for jump back
    SBlock block = hllBlocks.pop();              // pop the stack of {} blocks
    if (block.continueLabel != 0xFFFFFFFF) {
        // place label here as jump target for continue statements
        zeroAllMembers(code);
        code.label = block.continueLabel;
        code.section = section;
        codeBuffer.push(code);
    }
    // make loop instruction
    zeroAllMembers(code);
    code.section = section;
    code.line = linei;
    code.instruction = II_SUB_MAXLEN | II_JUMP_POSITIVE;
    code.reg1 = code.dest = block.codeBuffer2num;
    code.value.u = block.codeBuffer2index & 0xF;  // element type in vector
    code.dtype = TYP_INT64;
    code.sym5 = block.jumpLabel;
    code.etype = XPR_INT | XPR_REG | XPR_REG1 | XPR_JUMPOS;
    // fit code and save it
    checkCode1(code);
    fitCode(code); 
    if (lineError) return;     
    codeBuffer.push(code);  // save instruction
    // make break label
    if (block.breakLabel != 0xFFFFFFFF) {
        zeroAllMembers(code);
        code.section = section;
        code.label = block.breakLabel;
        codeBuffer.push(code);   // save label
    }
}
 
 
// Interpret switch statement in assembly code
void CAssembler::codeSwitch() {
    /*
    CMetaBuffer<CDynamicArray<SCcaseLabel> > caseLabels;
    CDynamicArray<SCode> extraCode;
    if (caseLabels.numEntries() == 0) caseLabels.setNum(switchNumber);
    */
 
 
}
 
// Interpret switch case label in assembly code
void CAssembler::codeCase() {}
 
// Finish switch statement at end bracket
void CAssembler::codeSwitch2() {
}
 
// Interpret break or continue statement in assembly code
void CAssembler::codeBreak() {
    uint32_t symi = findBreakTarget(tokens[tokenB].id);
    if (symi == 0) {
        errors.report(tokens[tokenB].pos, tokens[tokenB].stringLength, tokens[tokenB].id == HLL_BREAK ? ERR_MISPLACED_BREAK : ERR_MISPLACED_CONTINUE);
        return;
    }
    // make jump to symi
    SCode code;
    zeroAllMembers(code);
    code.section = section;
    code.instruction = II_JUMP;
    code.etype = XPR_JUMPOS | XPR_SYM1;
    code.sym5 = symi;
 
    // check if it can be merged with previous instruction
    mergeJump(code);
 
    // finish code and save it
    checkCode1(code);
    if (lineError) return;    
    fitCode(code);       // find an instruction variant that fits
    if (lineError) return;     
    codeBuffer.push(code);// save code structure
}
 
 
// Find or make the target symbol of a break or continue statement
uint32_t CAssembler::findBreakTarget(uint32_t k) {
    // k is HLL_BREAK or HLL_CONTINUE
    int32_t blocki;        // block index
    uint32_t symi;         // symbol id
    bool found = false;    // target found
    // search backwords through blocks to find the first block that can have break/continue
    for (blocki = (int32_t)hllBlocks.numEntries() - 1; blocki >= 0; blocki--) {
        switch (hllBlocks[blocki].blockType) {
        case HL_FOR: case HL_FOR_IN: case HL_WHILE: case HL_DO_WHILE:
            // can have break and continue
            found = true;
            break;
        case HL_SWITCH:
            // can have break, but not continue
            if (k == HLL_BREAK) found = true;
            break;
        case HL_FUNC: case HL_SECTION:
            // stop search and fail
            return 0;
        }
        if (found) break;
    }
    if (!found) return 0;  // not found
    char prefix;
    // find symbol in block
    if (k == HLL_BREAK) {
        symi = hllBlocks[blocki].breakLabel;
        prefix = 'b';
    }
    else {
        symi = hllBlocks[blocki].continueLabel;
        prefix = 'c';
    }     
    if (symi != 0xFFFFFFFF) return symi;  // symbol has already been assigned
 
    // symbol has not been assigned yet. give it a name
    const char * blockName = 0;
    switch (hllBlocks[blocki].blockType) {
    case HL_FOR: case HL_FOR_IN:
        blockName = "for";  break;    
    case HL_WHILE: 
        blockName = "while";  break;    
    case HL_DO_WHILE:
        blockName = "do";  break;
    case HL_SWITCH:
        blockName = "switch";  break;
    default: return 0;
    }
    char name[32];
    sprintf(name, "@%s_%u_%c", blockName, hllBlocks[blocki].blockNumber, prefix);
    symi = makeLabelSymbol(name);
    symi = symbols[symi].st_name;
    // save name in block, in case it is needed again
    if (k == HLL_BREAK) {
        hllBlocks[blocki].breakLabel = symi;
    }
    else {
        hllBlocks[blocki].continueLabel = symi;
    }
    return symi;
}
 
 
// Make a symbol for branch label etc., address not known yet. Returns zero if already defined
uint32_t CAssembler::makeLabelSymbol(const char * name) {
    ElfFWC_Sym2 sym;
    zeroAllMembers(sym);
    sym.st_type = STT_FUNC;
    sym.st_other = STV_HIDDEN | STV_IP;
    sym.st_section = section;
    sym.st_name = symbolNameBuffer.putStringN(name, (uint32_t)strlen(name));
    uint32_t symi = addSymbol(sym);  // save symbol with name
    if (symi == 0) {
        errors.reportLine(ERR_SYMBOL_DEFINED);
    }
    return symi;
}
 
// Merge jump instruction with preceding arithmetic instruction.
// If successful, returns true and puts the result in code1
bool CAssembler::mergeJump(SCode & code2) {
    if (cmd.optiLevel == 0) return false;        // merge only if optimization is on
    if (code2.label) return false;               // cannot merge if there is a label between the two instructions
    if (codeBuffer.numEntries() == 0) return false; // no previous instruction to merge with
    SCode code1 = codeBuffer[codeBuffer.numEntries()-1]; // previous code
 
    if (code1.section != code2.section) return false; // must be in same section
    SCode code3 = code1 | code2;  // combined code
    code3.reg1 = code1.reg1;
    code3.dest = code1.dest;
    uint32_t type = code1.dtype;
    // first instruction cannot have memory operand or other special options
    if (code1.etype & (XPR_MEM | XPR_SYM1 | XPR_MASK | XPR_OPTION | XPR_OPTIONS | XPR_JUMPOS | XPR_ERROR)) return false;
    if (!(code2.etype & XPR_JUMPOS)) return false;
 
    /*if (code2.instruction == II_JUMP) {
        // combine unconditional jump with add/sub
        if (code2.etype & (XPR_MEM | XPR_OPTION | XPR_REG)) return false;
        if ((code1.instruction & ~1) != II_ADD) return false;  // only add and sub
        if (type & TYP_FLOAT) return false;
        // successful combination of add/sub and jump
        codeBuffer.pop();        // remove previous code from buffer
        code2 = code3;
        return true;
    } */
 
    // second instruction must test the result of the first instruction
    if (code1.dest != code2.reg1) return false;
    // must have same operand type
    if ((code1.dtype & 0xF) > (code2.dtype & 0xF)) {
        if (!(code2.dtype & TYP_PLUS)) return false;
    }
    if ((code1.dtype & 0xF) < (code2.dtype & 0xF)) {
        if (!(code1.dtype & TYP_PLUS)) return false;
        type = code2.dtype;
    }
    type |= code2.dtype & TYP_UNS;  // signed/unsigned distinction only relevant for second instruction
    code3.dtype = type;
 
    // check if constant bigger than 32 bits
    if ((type & XPR_INT) && !(type & TYP_UNS) && (code1.value.i < (int32_t)0x80000000 || code1.value.i > (int32_t)0x7FFFFFFF)) return false;
    if ((type & XPR_INT) && (type & TYP_UNS) && (code1.value.u > (uint64_t)0xFFFFFFFF)) return false;
    if ((type & XPR_FLT) && (type & 0xFF) > TYP_FLOAT32) return false;
 
    switch (code1.instruction) {
    case II_ADD: case II_SUB:
        if (type & TYP_FLOAT) return false;
        if (code1.instruction == II_ADD && code1.value.u == 1 && !(type & TYP_UNS)) {
            // check if it fits increment_compare/jump below/above
            code3.value.u = code2.value.u;
            /*
            uint32_t nbits = (1 << (type & 7)) * 8;  // number of bits in constant
            if ((code2.instruction & 0xFFFE00) == II_JUMP_NEGATIVE && (code2.etype & XPR_INT) 
            && (code3.value.u & (((uint64_t)1 << nbits) - 1)) != (uint64_t)1 << (nbits - 1)) { // check for overflow
                // convert jump_sbelow n to jump_sbeloweq n-1
                // or jump_saboveeq n to jump sabove n-1
                code3.value.u--;
                code3.instruction ^= II_JUMP_NEGATIVE ^ II_JUMP_POSITIVE ^ II_JUMP_INVERT;
            }*/            
            if ((code3.instruction & 0xFFFE00) == II_JUMP_POSITIVE || (code3.instruction & 0xFFFE00) == II_JUMP_NEGATIVE) {
                // successful combination of add 1 and jump_sbelow/above
                code3.instruction = (code3.instruction & 0xFFFF00) | II_INCREMENT;
                code3.etype = (code1.etype & ~XPR_IMMEDIATE) | code2.etype;
                codeBuffer.pop();        // remove previous code from buffer
                code2 = code3;
                return true;
            }
        }
        // add/sub + compare
        if (!(code2.etype & XPR_INT) || code2.value.i != 0 || (code2.instruction & 0xFF) != II_COMPARE) return false; // must compare against zero
 
        //??:
        if ((type & TYP_UNS) && (code3.instruction & 0xFFFE00) != II_JUMP_ZERO) return false;  // unsigned works only for == and !=
        // successful combination of add/sub and signed compare with zero
        code3.instruction = code1.instruction | (code2.instruction & 0xFFFF00);
        code3.etype = code1.etype | (code2.etype & ~(XPR_IMMEDIATE | XPR_OPTIONS));
        codeBuffer.pop();        // remove previous code from buffer
        code2 = code3;
        return true;
 
    case II_AND: case II_OR: case II_XOR:
    //case II_SHIFT_LEFT:  case II_SHIFT_RIGHT_U:
        // must compare for equal to zero
        if (!(code2.etype & XPR_INT) || code2.value.i != 0) return false;
        //if ((code2.instruction & 0xFFFE00) != II_JUMP_ZERO) return false;
        if ((code2.instruction & ~ II_JUMP_INVERT) != (II_JUMP_ZERO | II_COMPARE)) return false;
        // combine instruction codes
        code3.instruction = code1.instruction | (code2.instruction & 0xFFFF00);
        code3.etype = code1.etype | (code2.etype & ~XPR_IMMEDIATE);
        // successful combination of and/or/xor/shift and compare with zero
        codeBuffer.pop();        // remove previous code from buffer
        code2 = code3;
        return true;
    }
    // everything else fails
    return false;
}
 
// check if line contains unconditional direct jump and nothing else
uint32_t CAssembler::hasJump(uint32_t line) {
    if (cmd.optiLevel == 0) return 0;             // don't optimize jump if optimization is off
    if (line >= lines.numEntries()) return 0;     // there is no line here
    uint32_t tokB = lines[line].firstToken;
    uint32_t tokN = lines[line].numTokens;        // number of tokens in line
    if (tokN > 0 && tokens[tokB+tokN-1].type == TOK_OPR && tokens[tokB+tokN-1].id == ';') tokN--;  // ignore ';'
    lineError = false;
    if (tokN == 1 && tokens[tokB].type == TOK_HLL) {  // high level keyword
        if (tokens[tokB].id == HLL_BREAK || tokens[tokB].id == HLL_CONTINUE) {
            return findBreakTarget(tokens[tokB].id);  // break or continue statement
        }
    }
    if (tokN == 2 && tokens[tokB].type == TOK_INS && tokens[tokB].id == II_JUMP && tokens[tokB+1].type == TOK_SYM) {
        // jump symbol
       // return tokens[tokB+1].value.w;
        return tokens[tokB+1].id;
    }
    return 0;  // anything else
}
 
// interpret condition in if(), while(), and for(;;) statements
void CAssembler::interpretCondition(SCode & code) {
    if ((code.instruction & 0xFF) == II_COMPARE) {
        // compare instruction. condition is in option bits
        switch ((code.optionbits >> 1) & 3) {
        case 0:
            code.instruction |= II_JUMP_ZERO;  break;
        case 1:
            code.instruction |= (code.dtype & TYP_UNS) ? II_JUMP_CARRY : II_JUMP_NEGATIVE;  break;
        case 2:
            code.instruction |= (code.dtype & TYP_UNS) ? II_JUMP_UABOVE : II_JUMP_POSITIVE;  break;
        case 3:
            errors.reportLine(ERR_EXPECT_LOGICAL); // should not occur
        }
        if (code.optionbits & 1) code.instruction ^= II_JUMP_INVERT; // invert if bit 0    
        if (code.dtype & TYP_FLOAT) { // floating point. resolve ordered/unordered
            //if ((code.instruction & 0x7F00) == (II_JUMP_NOTZERO & 0x7F00)) code.optionbits |= 8;  // a != b is unordered. This has already been done in CAssembler::op2Registers
            if ((code.optionbits & 8) && (code.instruction & 0x7F00) - 0x1000 < 0x2000) {
                code.instruction ^= II_JUMP_UNORDERED; // unordered bit
            }        
        }
    }
    else if ((code.instruction & 0xFF) == II_AND && (code.etype & XPR_INT)) {
        // if (r1 & )
        if (code.value.u != 0 && (code.value.u & (code.value.u-1)) == 0) {
            // n is a power of 2. test_bit
            code.instruction = II_TEST_BIT | II_JUMP_TRUE;
            code.value.u = bitScanReverse(code.value.u);
        }
        else {
            code.instruction = II_TEST_BITS_OR | II_JUMP_TRUE;
        }
        if (code.optionbits & 4) code.instruction ^= II_JUMP_INVERT;
    }
    else if ((code.instruction & 0xFF) == II_TEST_BITS_AND && (code.etype & XPR_INT)) {
        code.instruction |= II_JUMP_TRUE;
        if (code.optionbits & 1) code.instruction ^= II_JUMP_INVERT;
    }
    else if (code.instruction == 0 && code.etype == XPR_INT) {
        // constant condition. always true or always false
        code.instruction = II_JUMP;
        if (code.value.i == 0) code.instruction |= II_JUMP_INVERT;
 
        //!!
        code.etype = 0;
    }
    else {  // unrecognized expression
        errors.reportLine(ERR_EXPECT_LOGICAL);
        code.instruction = II_JUMP;
    }
    code.optionbits = 0;
}
 
 
// push registers on stack
void CAssembler::codePush() {   
    uint32_t state = 0;                     // 0: begin, 1: after type, 2: after 'push', 3: after '(', 
                                            // 4: after register 1, 5: after comma, 6: after register 2, 7: after comma,
                                            // 8: after constant, 9: after ')'
    int32_t reg1 = -1;                      // register  1, stack pointer if specified
    int32_t reg2 = -1;                      // register  2
    uint32_t imm = -1;                       // immediate operand
    uint32_t ot  = 3;                       // operand type
    uint32_t tok = 0;                       // token index
    SToken token;                           // token
    SCode code;                             // code structure
    zeroAllMembers(code);
    code.section = section;
    // loop through tokens on line
    for (tok = tokenB; tok < tokenB + tokenN; tok++) {
        token = tokens[tok]; 
        if (token.type == TOK_XPR && expressions[token.value.w].etype & XPR_REG) {
            // this is an alias for a register. Translate to register
            token.type = TOK_REG;
            token.id = expressions[token.value.w].reg1;
        }
 
        switch (state) {
        case 0:               // begin. expect type or 'push'
            if (token.id == HLL_PUSH) state = 2;
            else if (token.type == TOK_TYP) {
                ot = tokens[tok].id;
                state = 1;
            }
            else errors.report(token);
            break; 
 
        case 1:               // after type. expect 'push'
            if (token.id == HLL_PUSH) state = 2;
            else errors.report(token);
            break;
 
        case 2:  // after 'push'. expect '('
            if (token.type == TOK_OPR && token.id == '(') state = 3;
            else errors.report(token);
            break;
 
        case 3:  // after '('. expect register
            if (token.type != TOK_REG) {
                errors.report(token); return;
            }
            state = 4;
            reg1 = token.id;
            break;
 
        case 4:  // after register. expect ',' or ')'
            if (token.type == TOK_OPR && token.id == ',') state = 5;
            else if (token.type == TOK_OPR && token.id == ')') state = 9;
            else errors.report(token);
            break;
 
        case 5:  // after comma. expect register or constant
            if (token.type == TOK_REG) {
                reg2 = token.id;  state = 6;
            }
            else if (token.type == TOK_NUM || token.type == TOK_SYM) {
                imm = expression(tok, 1, 0).value.w;  state = 8;
            }
            else errors.report(token);
            break;
 
        case 6:  // after second register. expect comma or ')'
            if (token.type == TOK_OPR && token.id == ',') state = 7;
            else if (token.type == TOK_OPR && token.id == ')') state = 9;
            else errors.report(token);
            break;
 
        case 7:  // after second comma. expect constant
            if (token.type == TOK_NUM || token.type == TOK_SYM) {
                imm = expression(tok, 1, 0).value.w;  state = 8;
            }
            else errors.report(token);
            break;
 
        case 8:  // after constant. expect ')'
            if (token.type == TOK_OPR && token.id == ')') state = 9;
            else errors.report(token);
            break;
 
        default:  // after ')'. expect nothing
            errors.report(token);
            break;
        }
    }
    if (state != 9) {
        errors.reportLine(ERR_UNFINISHED_INSTRUCTION);
        return;
    }
    if (reg2 == -1) {  // stack pointer not specified. use default stack pointer
        reg2 = reg1;  reg1 = 0x1F | REG_R;
    }
    if (int32_t(imm) == -1) {  // no immediate operand. push only one register
        imm = reg2 & 0x1F;
    }
    if ((imm & 0x1F) < uint32_t(reg2 & 0x1F)) {
        errors.reportLine(ERR_OPERANDS_WRONG_ORDER);
        return;
    }
    if (!(reg1 & REG_R)) {
        errors.reportLine(ERR_WRONG_OPERANDS);
        return;
    }
    if ((reg2 & REG_V) && (imm & 0x80)) {
        errors.reportLine(ERR_WRONG_OPERANDS);
        return;
    }
    code.instruction = II_PUSH;
    code.dest = reg1;
    code.reg1 = reg2;
    code.value.u = imm;
    code.etype = XPR_INT | XPR_REG | XPR_REG1;
    code.dtype = TYP_INT8 | (ot & 0xF);
    checkCode1(code);
    fitCode(code);
    if (lineError) return;
    codeBuffer.push(code);  // save code
}
 
 
// pop register from stack
void CAssembler::codePop() {
    uint32_t state = 0;                     // 0: begin, 1: after type, 2: after 'push', 3: after '(', 
                                            // 4: after register 1, 5: after comma, 6: after register 2, 7: after comma,
                                            // 8: after constant, 9: after ')'
    int32_t reg1 = -1;                      // register  1, stack pointer if specified
    int32_t reg2 = -1;                      // register  2
    uint32_t imm = -1;                       // immediate operand
    uint32_t ot = 3;                       // operand type
    uint32_t tok = 0;                       // token index
    SToken token;                           // token
    SCode code;                             // code structure
    zeroAllMembers(code);
    code.section = section;
    // loop through tokens on line
    for (tok = tokenB; tok < tokenB + tokenN; tok++) {
        token = tokens[tok];
        if (token.type == TOK_XPR && expressions[token.value.w].etype & XPR_REG) {
            // this is an alias for a register. Translate to register
            token.type = TOK_REG;
            token.id = expressions[token.value.w].reg1;
        }
        switch (state) {
        case 0:               // begin. expect type or 'push'
            if (token.id == HLL_POP) state = 2;
            else if (token.type == TOK_TYP) {
                ot = tokens[tok].id;
                state = 1;
            }
            else errors.report(token);
            break;
 
        case 1:               // after type. expect 'push'
            if (token.id == HLL_POP) state = 2;
            else errors.report(token);
            break;
 
        case 2:  // after 'push'. expect '('
            if (token.type == TOK_OPR && token.id == '(') state = 3;
            else errors.report(token);
            break;
 
        case 3:  // after '('. expect register
            if (token.type != TOK_REG) {
                errors.report(token); return;
            }
            state = 4;
            reg1 = token.id;
            break;
 
        case 4:  // after register. expect ',' or ')'
            if (token.type == TOK_OPR && token.id == ',') state = 5;
            else if (token.type == TOK_OPR && token.id == ')') state = 9;
            else errors.report(token);
            break;
 
        case 5:  // after comma. expect register or constant
            if (token.type == TOK_REG) {
                reg2 = token.id;  state = 6;
            }
            else if (token.type == TOK_NUM || token.type == TOK_SYM) {
                imm = expression(tok, 1, 0).value.w;  state = 8;
            }
            else errors.report(token);
            break;
 
        case 6:  // after second register. expect comma or ')'
            if (token.type == TOK_OPR && token.id == ',') state = 7;
            else if (token.type == TOK_OPR && token.id == ')') state = 9;
            else errors.report(token);
            break;
 
        case 7:  // after second comma. expect constant
            if (token.type == TOK_NUM || token.type == TOK_SYM) {
                imm = expression(tok, 1, 0).value.w;  state = 8;
            }
            else errors.report(token);
            break;
 
        case 8:  // after constant. expect ')'
            if (token.type == TOK_OPR && token.id == ')') state = 9;
            else errors.report(token);
            break;
 
        default:  // after ')'. expect nothing
            errors.report(token);
            break;
        }
    }
    if (state != 9) {
        errors.reportLine(ERR_UNFINISHED_INSTRUCTION);
        return;
    }
    if (reg2 == -1) {  // stack pointer not specified. use default stack pointer
        reg2 = reg1;  reg1 = 0x1F | REG_R;
    }
    if (int32_t(imm) == -1) {  // no immediate operand. push only one register
        imm = reg2 & 0x1F;
    }
    if ((imm & 0x1F) < uint32_t(reg2 & 0x1F)) {
        errors.reportLine(ERR_OPERANDS_WRONG_ORDER);
        return;
    }
    if (!(reg1 & REG_R)) {
        errors.reportLine(ERR_WRONG_OPERANDS);
        return;
    }
    if ((reg2 & REG_V) && (imm & 0x80)) {
        errors.reportLine(ERR_WRONG_OPERANDS);
        return;
    }
    code.instruction = II_POP;
    code.dest = reg1;
    code.reg1 = reg2;
    code.value.u = imm;
    code.etype = XPR_INT | XPR_REG | XPR_REG1;
    code.dtype = TYP_INT8 | (ot & 0xF);
    checkCode1(code);
    fitCode(code);
    if (lineError) return;
    codeBuffer.push(code);  // save code
}
 

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.