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

Subversion Repositories forwardcom

[/] [forwardcom/] [bintools/] [assem.h] - Rev 141

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

/****************************    assem.h    ***********************************
* Author:        Agner Fog
* Date created:  2017-04-17
* Last modified: 2021-05-26
* Version:       1.11
* Project:       Binary tools for ForwardCom instruction set
* Module:        assem.h
* Description:
* Header file for assembler
*
* Copyright 2017-2021 GNU General Public License http://www.gnu.org/licenses
*****************************************************************************/
#pragma once
 
// Define token types:
const int  TOK_EOF = 1;                // end of file
const int  TOK_NAM = 2;                // unidentified name
const int  TOK_LAB = 3;                // code label or function name
const int  TOK_VAR = 4;                // data label
const int  TOK_SEC = 5;                // section name
const int  TOK_INS = 6;                // instruction name
const int  TOK_OPR = 7;                // operator
const int  TOK_NUM = 8;                // integer number
const int  TOK_FLT = (TOK_NUM + 1);    // floating point number
const int  TOK_CHA = 0x0A;             // character or string in single quotes ' '
const int  TOK_STR = 0x0B;             // string in double quotes " "
const int  TOK_DIR = 0x11;             // section or function directive
const int  TOK_ATT = 0x12;             // attribute of sections, functions, or symbols. also some keywords
const int  TOK_TYP = 0x14;             // type identifier
const int  TOK_OPT = 0x15;             // options of instructions and operands
const int  TOK_REG = 0x16;             // register name
const int  TOK_SYM = 0x1A;             // symbol, constant, variable, function. id = symbol index
const int  TOK_XPR = 0x1B;             // expression. id = expression index
const int  TOK_HLL = 0x20;             // high level language block (if, for, etc.)
const int  TOK_ERR = 0x80;             // error. illegal character or unmatched quote
 
// Define keyword id's:
 
// Directives
const int DIR_SECTION  = ((TOK_DIR << 24) + 1);
const int DIR_FUNCTION = ((TOK_DIR << 24) + 2);
const int DIR_END      = ((TOK_DIR << 24) + 4);
const int DIR_PUBLIC   = ((TOK_DIR << 24) + 8);
const int DIR_EXTERN   = ((TOK_DIR << 24) + 0x10);
 
// Attributes of sections
const int ATT_READ     = ((TOK_ATT << 24) + SHF_READ);
const int ATT_WRITE    = ((TOK_ATT << 24) + SHF_WRITE);
const int ATT_EXEC     = ((TOK_ATT << 24) + SHF_EXEC);
const int ATT_ALIGN    = ((TOK_ATT << 24) + 0x10);
const int SECTION_LOCAL_VAR = 0xFFFFFFFF;                       // local constant with no section
 
// Attributes of variables, constants and functions
const int ATT_WEAK     = ((TOK_ATT << 24) + 0x20);              // weak public or weak external symbol
const int ATT_REGUSE   = ((TOK_ATT << 24) + 0x21);              // register use of function
const int ATT_CONSTANT = ((TOK_ATT << 24) + 0x10000);           // used for external constants
const int ATT_UNINIT   = ((TOK_ATT << 24) + 0x20000);           // uninitialized section (BSS)
const int ATT_COMDAT   = ((TOK_ATT << 24) + 0x40000);           // communal section. duplicates and unreferenced sections are removed
const int ATT_EXCEPTION= ((TOK_ATT << 24) + SHF_EXCEPTION_HND); // exception handler info
const int ATT_EVENT    = ((TOK_ATT << 24) + SHF_EVENT_HND);     // event handler info
const int ATT_DEBUG    = ((TOK_ATT << 24) + SHF_DEBUG_INFO);    // debug info
const int ATT_COMMENT  = ((TOK_ATT << 24) + SHF_COMMENT);       // comments
 
// Type definitions
const int TYP_INT8     = ((TOK_TYP << 24) + 0x10);
const int TYP_INT16    = ((TOK_TYP << 24) + 0x11);
const int TYP_INT32    = ((TOK_TYP << 24) + 0x12);
const int TYP_INT64    = ((TOK_TYP << 24) + 0x13);
const int TYP_INT128   = ((TOK_TYP << 24) + 0x14);
const int TYP_UNS                         = 0x20;   // add this for unsigned integer types
const int TYP_PLUS                       = 0x100;   // add this larger type allowed
const int TYP_FLOAT16  = ((TOK_TYP << 24) + 0x44);
const int TYP_FLOAT32  = ((TOK_TYP << 24) + 0x45);
const int TYP_FLOAT64  = ((TOK_TYP << 24) + 0x46);
const int TYP_FLOAT128 = ((TOK_TYP << 24) + 0x47);
const int TYP_INT      =                    0x10;    // generic test for int types
const int TYP_FLOAT    =                    0x40;    // generic test for float types
const int TYP_STRING   = ((TOK_TYP << 24) + 0x18);
 
// Options and attributes of instructions
const int OPT_MASK     = ((TOK_OPT << 24) + 1);
const int OPT_FALLBACK = ((TOK_OPT << 24) + 2);
const int OPT_LENGTH   = ((TOK_OPT << 24) + 3);
const int OPT_BROADCAST= ((TOK_OPT << 24) + 4);
const int OPT_LIMIT    = ((TOK_OPT << 24) + 5);
const int OPT_SCALAR   = ((TOK_OPT << 24) + 6);
const int OPT_OPTIONS  = ((TOK_OPT << 24) + 7);
 
// Register types
const int REG_R        =  0x20;        // general purpose register
const int REG_V        =  0x40;        // vector register
const int REG_SPEC     =  0x60;        // special register, accessed with read_spec and write_spec instructions
const int REG_CAPAB    =  0x80;        // capabilities register, accessed with read_capabilities
const int REG_PERF     =  0xA0;        // performance counter, accessed with read_perf
const int REG_SYS      =  0xC0;        // system register, accessed with read_sys and write_sys
const int REG_OTHER    = 0x100;        // other register, unclassified
 
// ID for special registers:
// bit 0-4   is the id used when reading or writing the register
// bit 5-7   indicate the type of register
// bit 16-20 is the id when the register is used as base pointer
// bit 24-31 is token type
const int REG_NUMCONTR = ((TOK_REG << 24) +                REG_SPEC + 0);    // numeric control register, default flag
const int REG_THREADP  = ((TOK_REG << 24) + (0x1C << 16) + REG_SPEC + 1);    // thread data pointer
const int REG_DATAP    = ((TOK_REG << 24) + (0x1D << 16) + REG_SPEC + 2);    // data section pointer
const int REG_IP       = ((TOK_REG << 24) + (0x1E << 16) + REG_OTHER   );    // instruction pointer, changed by jump instructions
const int REG_SP       = ((TOK_REG << 24) + (0x1F << 16) + REG_R + 0x1F);    // stack pointer
 
// high level language directives
const int HLL_IF       = ((TOK_HLL << 24) + 1);
const int HLL_ELSE     = ((TOK_HLL << 24) + 2);
const int HLL_SWITCH   = ((TOK_HLL << 24) + 3);
const int HLL_CASE     = ((TOK_HLL << 24) + 4);
const int HLL_FOR      = ((TOK_HLL << 24) + 5);
const int HLL_IN       = ((TOK_HLL << 24) + 6);
const int HLL_NOCHECK  = ((TOK_HLL << 24) + 7);
const int HLL_WHILE    = ((TOK_HLL << 24) + 8);
const int HLL_DO       = ((TOK_HLL << 24) + 9);
const int HLL_BREAK    = ((TOK_HLL << 24) + 10);
const int HLL_CONTINUE = ((TOK_HLL << 24) + 11);
 
const int HLL_FALSE    = ((TOK_HLL << 24) + 20);
const int HLL_TRUE     = ((TOK_HLL << 24) + 21);
 
// push and pop may be replaced by macros later:
const int HLL_PUSH     = ((TOK_HLL << 24) + 12);
const int HLL_POP      = ((TOK_HLL << 24) + 13);
 
// line types
const int LINE_DATADEF =    1;                   // data definition
const int LINE_CODEDEF =    2;                   // code instruction
const int LINE_PUBLICDEF =  3;                   // public symbol definition
const int LINE_METADEF =    4;                   // assemble-time definitions and metaprogramming
const int LINE_OPTIONS =    5;                   // option setting
const int LINE_FUNCTION= 0x11;                   // function definition
const int LINE_SECTION = 0x12;                   // section definition
const int LINE_ENDDIR  = 0x10;                   // function or section end
const int LINE_ERROR   = 0xFF;                   // error detected in this line
 
// Operator id's are equal to the ASCII code with these additions:
const int EQ = 0x100;                  // operator followed by equal sign, e.g. +=
const int D2 = 0x200;                  // operator repeated, e.g. <<
const int D3 = 0x400;                  // operator triple, e.g. >>>
const int OP_UNS = 0x1000;             // unsigned operation
 
// SExpression types in .etype
const int XPR_INT           = 0x01;    // contains integer value
const int XPR_INT2          = 0x02;    // contains a second integer constant in the upper half of value
const int XPR_FLT           = 0x04;    // contains floating point value
const int XPR_IMMEDIATE     = 0x07;    // contains immediate constant
const int XPR_STRING        = 0x08;    // contains string (u = string buffer entry, sym2 = length)
const int XPR_REG           = 0x10;    // contains register operand
const int XPR_OP            = 0x20;    // contains instruction or operator
const int XPR_OPTION        = 0x80;    // contains option keyword for memory operand
const int XPR_MEM          = 0x100;    // contains memory operand, or part of it
const int XPR_SYM1         = 0x200;    // contains symbol address
const int XPR_SYM2         = 0x400;    // contains reference symbol address
const int XPR_SYMSCALE     = 0x800;    // contains scale factor on (sym1-sym2)
const int XPR_REG1        = 0x1000;    // contains first register operand
const int XPR_REG2        = 0x2000;    // contains second register operand
const int XPR_REG3        = 0x4000;    // contains third register operand in value.u
const int XPR_BASE        = 0x8000;    // contains base register
const int XPR_INDEX      = 0x10000;    // contains index register and scale factor
const int XPR_OFFSET     = 0x20000;    // contains memory address offset
const int XPR_LIMIT      = 0x40000;    // contains limit for index
const int XPR_SCALAR     = 0x80000;    // contains scalar memory operand without broadcast
const int XPR_LENGTH    = 0x100000;    // contains vector length register
const int XPR_BROADC    = 0x200000;    // contains vector broadcast length register
const int XPR_MASK      = 0x400000;    // contains mask register
const int XPR_FALLBACK  = 0x800000;    // contains fallback register
const int XPR_OPTIONS  = 0x1000000;    // contains options or signbits in IM3
const int XPR_JUMPOS   = 0x2000000;    // contains self-relative jump offset
const int XPR_TYPENAME =0x10000000;    // contains type name in value
const int XPR_UNRESOLV =0x40000000;    // contains unresolved name or value
const int XPR_ERROR    =0x80000000;    // an error occurred during the generation
 
// Instruction id's
const uint32_t II_NOP            =  0x30000;
const uint32_t II_STORE          =        1;
const uint32_t II_MOVE           =        2;
const uint32_t II_COMPARE        =        7;
const uint32_t II_ADD            =        8;
const uint32_t II_SUB            =        9;
const uint32_t II_SUB_REV        =       10;
const uint32_t II_MUL            =       11;
const uint32_t II_MUL_HI         =       12;
const uint32_t II_MUL_EX         =  0x1201A;
const uint32_t II_DIV            =       14;
const uint32_t II_DIV_U          =       15; // all unsigned variants must be signed variant | 1
const uint32_t II_DIV_REV        =       16;
const uint32_t II_DIV_EX         =  0x12018;
const uint32_t II_REM            =       18;
const uint32_t II_REM_U          =       19;
const uint32_t II_MIN            =       20;
const uint32_t II_MIN_U          =       21;
const uint32_t II_MAX            =       22;
const uint32_t II_MAX_U          =       23;
const uint32_t II_AND            =       26; 
const uint32_t II_OR             =       27; 
const uint32_t II_XOR            =       28; 
const uint32_t II_SHIFT_LEFT     =       32;
const uint32_t II_MUL_2POW       =       32;
const uint32_t II_ROTATE         =       33;
const uint32_t II_SHIFT_RIGHT_S  =       34;
const uint32_t II_SHIFT_RIGHT_U  =       35;  // must be = II_SHIFT_RIGHT_S | 1
const uint32_t II_CLEAR_BIT      =       36;
const uint32_t II_SET_BIT        =       37;
const uint32_t II_TOGGLE_BIT     =       38;
const uint32_t II_TEST_BIT       =       39;
const uint32_t II_TEST_BITS_AND  =       40;
const uint32_t II_TEST_BITS_OR   =       41;
const uint32_t II_MUL_ADD        =       49;
const uint32_t II_MUL_ADD2       =       50;
const uint32_t II_ADD_ADD        =       51;
const uint32_t II_SELECT_BITS    =       52;
const uint32_t II_FUNNEL_SHIFT   =       53;
const uint32_t II_SHIFT_U_ADD    =   0x0101;
//const uint32_t II_MOVE_U         =   0x11001;
const uint32_t II_ADD_H          =  0x50008;  // float16
const uint32_t II_SUB_H          =  0x50009;  // float16
const uint32_t II_MUL_H          =  0x5000B;  // float16
const uint32_t II_DIV_H          =  0x50010;  // float16
const uint32_t II_MUL_ADD_H      =  0x50031;  // float16
const uint32_t II_PUSH           =  0x18038;
const uint32_t II_POP            =  0x18039;
const uint32_t II_REPLACE        =  0xA0001;
const uint32_t II_REPLACE_EVEN   =  0x26004;
const uint32_t II_REPLACE_ODD    =  0x26005;
const uint32_t II_ADDRESS        =  0x29020;
 
// constants for jump and branch instrucions. May be combined with II_ADD, II_SUB, II_COMPARE, etc. 
const uint32_t II_INCREMENT      =   0x0051;  // increment. combine with II_JUMP_POSITIVE
const uint32_t II_SUB_MAXLEN     =   0x0052;  // subtract max vector length. combine with II_JUMP_POSITIVE
const uint32_t II_FP_CATEGORY    =   0x0054;  // fp_category. combine with II_JUMP_TRUE
 
const uint32_t II_JUMP           = 0x101000;  // jump codes may be combined with II_ADD etc.
const uint32_t II_JUMP_ZERO      = 0x101200;  // xor with 0x100 for opposite condition
const uint32_t II_JUMP_NOTZERO   = 0x101300;  // not zero or not equal
const uint32_t II_JUMP_NEGATIVE  = 0x101400;  // negative or signed below
const uint32_t II_JUMP_POSITIVE  = 0x101600;  // positive or signed above
const uint32_t II_JUMP_OVERFLOW  = 0x101800;  // signed overflow
const uint32_t II_JUMP_CARRY     = 0x102000;  // carry, borrow, unsigned below, abs below. Reverse condition if 'sub n' replaced by 'add (-n)'
const uint32_t II_JUMP_UBELOW    = 0x102000;  // carry, borrow, unsigned below, abs below. Reverse condition if 'sub n' replaced by 'add (-n)'
const uint32_t II_JUMP_UABOVE    = 0x102200;  // unsigned above, abs above
const uint32_t II_JUMP_TRUE      = 0x102400;  // bit test etc. true
const uint32_t II_JUMP_FALSE     = 0x102500;  // bit test etc. false
const uint32_t II_JUMP_INVERT    =   0x0100;  // flip this bit to invert condition
const uint32_t II_JUMP_UNORDERED =   0x8000;  // flip this bit to jump if unordered
const uint32_t II_JUMP_INSTR     = 0x100000;  // bit to identify direct jump and call instructions
const uint32_t II_INCREMENT_COMPARE_JBELOW = 48; // opj for increment_compare_jump_below
const uint32_t II_CALL           = 0x111000;  // direct call
 
const uint32_t II_ALIGN        = 0x10000000;  // align directive
const uint32_t II_OPTIONS      = 0x20000000;  // options directive
 
const int MAX_ALIGN              =     4096;  // maximum allowed alignment  (note: if changed, change also in error.cpp at ERR_ALIGNMENT)
 
// Bit values generated by fitConstant() and stored in SCode::fitNumX
// Indicates how many bits are needed to contain address offset or immediate constant of an instruction
const int IFIT_I8        =     0x10;  // fits into signed 8-bit integer
const int IFIT_J8        =     0x20;  // (-x) fits into signed 8-bit integer
const int IFIT_U8        =     0x40;  // x fits into unsigned 8-bit integer
const int IFIT_I8SHIFT   =     0x80;  // fits into signed 8-bit integer with left shift
const int IFIT_I16       =    0x100;  // fits into signed 16-bit integer
const int IFIT_J16       =    0x200;  // (-x) fits into signed 16-bit integer
const int IFIT_U16       =    0x400;  // fits into unsigned 16-bit integer
const int IFIT_I16SHIFT  =    0x800;  // fits into signed 16-bit integer with left shift
const int IFIT_I16SH16   =   0x1000;  // fits into signed 16-bit integer shifted left by 16
const int IFIT_I24       =   0x4000;  // fits into signed 24-bit signed integer
const int IFIT_I32       =  0x10000;  // fits into signed 32-bit integer
const int IFIT_J32       =  0x20000;  // (-x) fits into signed 32-bit integer
const int IFIT_U32       =  0x40000;  // fits into unsigned 32-bit integer
const int IFIT_I32SHIFT  =  0x80000;  // fits into signed 32-bit integer with left shift
const int IFIT_I32SH32   = 0x100000;  // fits into 32-bit integer shifted left by 32
const int IFIT_J         =  (IFIT_J8 | IFIT_J16 | IFIT_J32); // (-x) fits better than x
const int FFIT_16       = 0x1000000;  // fits into normal half precision
const int FFIT_32       = 0x2000000;  // fits into normal single precision
const int FFIT_64       = 0x4000000;  // fits into double precision
const int IFIT_RELOC   = 0x10000000;  // relocation record needed
const int IFIT_LARGE   = 0x20000000;  // choose the larger size if uncertain. This input is used if optimization process has convergence problems
 
// values for immediate operand types
//const int OPI_INT4            =   1;  // int4
const int OPI_INT8            =   2;  // int8
const int OPI_INT16           =   3;  // int16
const int OPI_INT32           =   4;  // int32
const int OPI_INT64           =   5;  // int64
const int OPI_INT8SH          =   6;  // int8 << i
const int OPI_INT16SH         =   7;  // int16 << i
const int OPI_INT16SH16       =   8;  // int16 << 16
const int OPI_INT32SH32       =   9;  // int32 << 32
const int OPI_UINT8           =  18;  // uint8
const int OPI_UINT16          =  19;  // uint16
const int OPI_UINT32          =  20;  // uint32
const int OPI_UINT64          =  21;  // uint64
const int OPI_2INT8           =  24;  // int8+int8
const int OPI_INT886          =  25;  // int8+int8+int6
const int OPI_2INT16          =  26;  // int16+int16
const int OPI_INT1632         =  27;  // int16+int32
const int OPI_2INT32          =  28;  // int32+int32
const int OPI_INT1688         =  29;  // int16+int8+int8
const int OPI_INT8F           =  34;  // int8 converted to float
const int OPI_INT16F          =  35;  // int16 converted to float
const int OPI_FLOAT16         =  64;  // float16
const int OPI_FLOAT32         =  65;  // float32
const int OPI_FLOAT64         =  66;  // float64
const int OPI_IMPLICIT        =  99;  // implicit immediate operand (usually uint8)
const int OPI_OT              = 100;  // determined by operand type field
 
 
// struct SLine contains information about each line in the input file
struct SLine {
    uint16_t type;                // line type: LINE_DATADEF, etc
    uint16_t sectionType;         // section flags
    uint32_t beginPos;            // position in input file
    uint32_t firstToken;          // index to first token
    uint32_t numTokens;           // number of tokens in line
    uint32_t file;                // file of origin. (1 = source file, 2+ = include files, 0x1000+ = meta-generated lines)
    uint32_t linenum;             // line number in file of origin
};
 
// struct SToken is used for splitting each line into tokens
struct SToken {
    uint32_t type;                // Token type
    uint32_t id;                  // ID if known name or operator
    uint32_t pos;                 // File offset
    uint32_t stringLength;        // Length of token as string
    uint16_t priority;            // Priority if operator
    uint16_t vartype;             // 0: value not known, 3: int64, 5: double, 8: string
    uint32_t unused;
    union {                       // value if constant or assemble-time variable
        uint64_t u;
        int64_t  i;
        double   d;
        uint32_t w;
    } value;
};
 
// struct SOperator is used for list of operators
struct SOperator {
    char name[8];                 // name
    uint32_t id;                  // identifier
    uint32_t priority;            // priority if operator
};
 
// operator < for sorting operator list
static inline bool operator < (SOperator const & a, SOperator const & b) {
    return strcmp(a.name, b.name) < 0;
}
 
// struct SKeyword is used for list of keywords
struct SKeyword {
    char name[28];                // name
    uint32_t id;                  // identifier
};
 
// struct SExpression is used during assemble-time evaluation of expressions containing
// any type of operands: integer, float, string, registers, memory operands, options
struct SExpression {
    union {                       // immediate operand value
        int64_t  i;               // as signed
        uint64_t u;               // as unsigned
        double   d;               // as double
        uint32_t w;               // as unsigned 32 bit integer
    } value;
    int32_t  offset_mem;          // offset for memory operand
    int32_t  offset_jump;         // offset for jump
    uint32_t etype;               // flags for elements in expression: XPR_...
    uint32_t tokens;              // number of tokens used
    uint32_t sym1;                // first symbol of memory operand, indexed by namebuffer offset
    uint32_t sym2;                // reference symbol of memory operand, indexed by namebuffer offset
    uint32_t sym3;                // first symbol of immediate operand, indexed by namebuffer offset
    uint32_t sym4;                // reference symbol of immediate operand, indexed by namebuffer offset
    uint32_t sym5;                // symbol for jump target, indexed by namebuffer offset
    uint32_t instruction;         // instruction corresponding to operator
    uint8_t  optionbits;          // option bits or sign bits
    uint8_t  base;                // base register of memory operand    
    uint8_t  index;               // index register of memory operand
    uint8_t  length;              // length or broadcast register of memory operand
    int8_t   scale;               // scale factor for index register
    uint8_t  symscale1;           // scale factor for sym1-sym2    
    uint8_t  symscale3;           // scale factor for sym3-sym4
    uint8_t  mask;                // mask register
    uint8_t  reg1;                // first register operand
    uint8_t  reg2;                // second register operand
    uint8_t  reg3;                // third register operand   
    uint8_t  fallback;            // fallback register
};
 
 
// struct SCode is the result of interpreting a line of code containing an instruction
struct SCode : public SExpression {
    SFormat  const * formatp;     // instruction format. pointer to record in formatList in disassem1.cpp, or a copy of it
    uint32_t line;                // entry into lines buffer
    uint32_t section;             // code section
    uint32_t address;             // address relative to begin of section in current module
    uint32_t label;               // a code or data label, identified by an index into symbolNameBuffer (not an index into 'symbols' because this may change when new symbols are added)
    uint32_t dtype;               // data type. (TYP_INT8 etc.)
    uint32_t instr1;              // index to instruction in instructionlist
    uint32_t fitNum;              // indicates if immediate constant fits a certain representation (from fitInteger or fitFloat function)
    uint32_t fitAddr;             // indicates if relative address fits a certain number of bits
    uint32_t fitJump;             // indicates if relative jump offset fits a certain number of bits
    uint8_t  dest;                // destination register (2 = memory destination)
    uint8_t  numOp;               // number of source operands
    uint8_t  size;                // size of instruction. minimum size if actual size depends on unresolved cross references
    uint8_t  sizeUnknown;         // actual size may be up to this value bigger
    uint8_t  category;            // instruction category
};
 
 
// struct SBlock is used for tracking {} code blocks
struct SBlock  {
    uint32_t blockType;           // block type. see definitions of HL_FUNC etc. in assem5.cpp
    uint32_t blockNumber;         // sequential number used in label names
    uint32_t startBracket;        // token of start '{'
    uint32_t jumpLabel;           // target label for jump, else, or loop
    uint32_t breakLabel;          // target label for break statement. -1 if break is possible but label not yet defined
    uint32_t continueLabel;       // target label for continue statement. -1 if continue is possible but label not yet defined
    uint32_t codeBuffer2index;    // index of entry in codeBuffer2
    uint32_t codeBuffer2num;      // number of instruction codes in codeBuffer2
};
 
// combine contents of two expressions
static inline SExpression operator | (SExpression const & exp1, SExpression const & exp2) {
    SExpression expr;
    for (uint32_t i = 0; i < sizeof(SExpression) / sizeof(uint64_t); i++) {
        (&expr.value.u)[i] = (&exp1.value.u)[i] | (&exp2.value.u)[i];
    }
    return expr;
}
 
static inline SCode operator | (SCode const & code1, SExpression const & exp2) {
    SCode code0 = code1;
    for (uint32_t i = 0; i < sizeof(SExpression) / sizeof(uint64_t); i++) {
        (&code0.value.u)[i] = (&code1.value.u)[i] | (&exp2.value.u)[i];
    }
    return code0;
}
 
// find the smallest representation that the floating point operand fits into
int fitFloat(double x);
 
// insert memory operand into code structure
void insertMem(SCode & code, SExpression & expr);
 
// insert everything from expression to code structure, OR'ing all bits
void insertAll(SCode & code, SExpression & expr);
 
// operator < for sorting keyword list
static inline bool operator < (SKeyword const & a, SKeyword const & b) {
    // case insensitive compare. This function is not standardized. make my own:
    return strncasecmp_(a.name, b.name, 1000) < 0;
 
#if defined (_MSC_VER)
    //return _stricmp(a.name, b.name) < 0;    // microsoft
#else
    //return strcasecmp(a.name, b.name) < 0;  // unix
#endif
 
}
 
// redefine symbol structure sorted by name
struct ElfFWC_Sym2 : public ElfFwcSym {
};
 
 
static inline bool operator < (ElfFWC_Sym2 const & a, ElfFWC_Sym2 const & b) {
    return strcmp(symbolNameBuffer.getString(a.st_name), symbolNameBuffer.getString(b.st_name)) < 0;
}
 
static inline bool operator == (ElfFWC_Sym2 const & a, ElfFWC_Sym2 const & b) {
    return strcmp(symbolNameBuffer.getString(a.st_name), symbolNameBuffer.getString(b.st_name)) == 0;
}
 
// structure in list of assembly errors
struct SAssemError {
    uint32_t pos;                                // position in input file
    uint32_t stringLength;                       // length of token string
    uint32_t file;                               // File where error was detected
    uint16_t num;                                // Error id
    uint16_t pass;                               // Pass during which error occurred
};
 
class CAssembler;                                // Forward definition
 
// class for reporting errors in assembly file
class CAssemErrors {
public:
    CAssemErrors();
    void report(uint32_t position, uint32_t stringLength, uint32_t num); // Report an error
    void report(SToken const & token);           // Report an error, pointing to a specific token
    void reportLine(uint32_t num);               // Report an error in current line
    void setOwner(CAssembler * a);               // Give access to CAssembler
    uint32_t numErrors();                        // Return number of errors
    bool tooMany();                              // true if too many errors
    void outputErrors();                         // Write all errors to stderr
protected:
    CAssembler * owner;
    CDynamicArray<SAssemError>list;              // List of errors
    uint32_t maxErrors;                          // Maximum number of errors to report
};
 
 
// class CDisassembler handles disassembly of ForwardCom ELF file
class CAssembler : public CFileBuffer {
public:
    CAssembler();                                // Constructor
    void go();
protected:
    friend class CAssemErrors;                   // This class handles error messages
    uint32_t iInstr;                             // Position of current instruction relative to section start
    uint32_t instrLength;                        // Length of current instruction, in 32-bit words
    uint32_t operandType;                        // Operand type of current instruction
    uint32_t format;                             // Format of current instruction
    uint64_t variant;                            // Template variant and options
    int64_t  value0;                             // original value of immediate operand   
    uint32_t tokenB;                             // index to first token in current line
    uint32_t tokenN;                             // number of tokens in current line
    uint32_t dataType;                           // data type for current instruction
    uint32_t section;                            // Current section
    uint32_t sectionFlags;                       // current section information flags
    uint32_t linei;                              // index to current line
    uint32_t filei;                              // index to current input file
    uint32_t pass;                               // what pass are we in
    uint32_t iLoop;                              // index of current loop statement
    uint32_t iIf;                                // index of current 'if' statement
    uint32_t iSwitch;                            // index of current 'switch' statement    
    uint32_t numSwitch;                          // total number of 'switch' statements
    bool     lineError;                          // error in current line. stop interpreting
    uint64_t code_size;                          // codesize option determines code address sizes
    uint64_t data_size;                          // datasize option determines data address sizes
    STemplate const * pInstr;                    // Pointer to current instruction code
    SInstruction2 const * iRecord;               // Pointer to instruction table entry
    SFormat const * fInstr;                      // Format details of current instruction code
    CELF outFile;                                // Output file
    CDynamicArray<SToken> tokens;                // List of tokens
    CDynamicArray<SLine> lines;                  // Information about each line of the input file
    CDynamicArray<SInstruction> instructionlist; // List of instruction set, unsorted
    CDynamicArray<SInstruction> instructionlistNm;// List of instruction set, sorted by name
    CDynamicArray<SInstruction3> instructionlistId; // List of instruction set, sorted by id
    CDynamicArray<SOperator> operators;          // List of operators
    CDynamicArray<SKeyword> keywords;            // List of keywords
    CDynamicArray<ElfFWC_Sym2> symbols;          // List of symbols
    CDynamicArray<ElfFwcReloc> relocations;     // List of relocations
    CDynamicArray<uint8_t> brackets;             // Stack of nested brackets during evaluation of expression
    CDynamicArray<SCode> codeBuffer;             // Coded instructions
    CDynamicArray<SCode> codeBuffer2;            // Temporary storage of instructions for loops and switch statements
    CDynamicArray<ElfFwcShdr> sectionHeaders;    // Section headers
    CDynamicArray<SFormat> formatList3;          // Subset of formatList for multiformat instruction formats
    CDynamicArray<SFormat> formatList4;          // Subset of formatList for jump instruction formats
    CDynamicArray<SBlock>  hllBlocks;            // Tracking of {} blocks    
    CDynamicArray<SExpression> expressions;      // Expressions saved as assemble-time symbols    
    CTextFileBuffer stringBuffer;                // Buffer for assemble-time string variables
    CMetaBuffer<CMemoryBuffer> dataBuffers;      // databuffer for each section
    CAssemErrors errors;                         // Error reporting
    void initializeWordLists();                  // Initialize and sort instruction list, operator list, and keyword list
    void feedBackText1();                        // write feedback text on stdout
    void pass1();                                // Split input file into lines and tokens. Handle preprocessing directives. Find symbol definitions
    void interpretSectionDirective();            // Interpret section directive during pass 2 or 3
    void interpretFunctionDirective();           // Interpret function directive during pass 2 or 3
    void interpretEndDirective();                // Interpret section or function end directive during pass 2 or 3
    void interpretOptionsLine();                 // Interpret line specifying options
    uint32_t addSymbol(ElfFWC_Sym2 & sym);       // Add a symbol to symbols list
    uint32_t findSymbol(uint32_t name);          // Find symbol by index into symbolNameBuffer
    uint32_t findSymbol(const char * name, uint32_t len); // Find symbol by name with specified length
    void pass2();                                // A. Handle metaprogramming directives
                                                 // B. Classify lines
                                                 // C. Identify symbol names, sections, labels, functions 
    void interpretExternDirective();             // Interpret extern directive during pass 2
    void interpretPublicDirective();             // Interpret public directive during pass 2
    void interpretLabel(uint32_t tok);           // Interpret code or data label during pass 2
    void interpretVariableDefinition1();         // interpret assembly style variable definition
    void interpretVariableDefinition2();         // interpret C style variable definition
    void determineLineType();                    // check if line is code or data
    void interpretAlign();                       // interpret code or data alignment directive
    void interpretMetaDefinition();              // Interpret line beginning with '%' containing meta code
    void replaceKnownNames();                    // Replace known symbol names with symbol references and meta variables with their value
    SExpression expression(uint32_t tok1, uint32_t ntok, uint32_t option); // Interpret and evaluate expression
    SExpression symbol2expression(uint32_t symi); // make expression out of symbol
    SExpression op1minus(SExpression & exp1);    // Interpret -(A+B), etc.
    SExpression op2(uint32_t op, SExpression & exp1, SExpression & exp2); // Interpret dyadic expression with any type of operands
    SExpression op2Int(uint32_t op, SExpression const & exp1, SExpression const & exp2); // Interpret dyadic expression with integer operands
    SExpression op2Float(uint32_t op, SExpression & exp1, SExpression & exp2); // Interpret dyadic expression with floating point operands
    SExpression op2String(uint32_t op, SExpression const & exp1, SExpression const & exp2); // Interpret dyadic expression with string operands
    SExpression op2Registers(uint32_t op, SExpression const & exp1, SExpression const & exp2); // Interpret dyadic expression with register operands
    SExpression op2Memory(uint32_t op, SExpression & exp1, SExpression & exp2); // Interpret dyadic expression with memory operands
    SExpression op3(uint32_t tok1, uint32_t toklow, uint32_t tokcolon, uint32_t maxtok, uint32_t options); // Interpreted triadic expression exp1 ? exp2 : exp3 at the indicated positions
    void assignMetaVariable(uint32_t symi, SExpression & expr, uint32_t typetoken); // define or modify assemble-time constant or variable
    void pass3();                                // Generate code and data
    void makeFormatLists();                      // extract subsets of formatList into formatList3 and formatList4
    void interpretCodeLine();                    // Interpret a line defining code
    int  fitCode(SCode & code);                  // find an instruction variant that fits the code
    bool instructionFits(SCode const & code, SCode & codeTemp, uint32_t ii); // check if instruction fits into specified format
    bool jumpInstructionFits(SCode const & code, SCode & codeTemp, uint32_t ii); // check if jump instruction fits into specified format
    int  fitConstant(SCode & code);              // check how many bits are needed to contain immediate constant in an instruction.
    int  fitAddress(SCode & code);               // check how many bits are needed to contain relative address in an instruction.
    void checkCode1(SCode & code);               // eheck code for correctness before fitting a format, and fix some code details
    void checkCode2(SCode & code);               // eheck register types etc. after fitting a format, and finish code details
    uint32_t checkCodeE(SCode & code);           // find reason why no format fits, and return error number
    void optimizeCode(SCode & code);             // optimize instruction. replace by more efficient instruction if possible
    void pass4();                                // Resolve symbol addresses and cross references, optimize forward references
    void pass5();                                // Make binary file
    void copySections();                         // copy sections to outFile
    void copySymbols();                          // copy symbols to outFile
    //void removePrivateSymbols();               // remove local symbols and adjust relocation records with new symbol indexes
    void makeListFile();                         // make output listing
    int64_t calculateMemoryOffset(SCode & code); // calculate memory address possibly involving symbol. generate relocation if necessary
    int64_t calculateJumpOffset(SCode & code);   // calculate jump offset possibly involving symbol. generate relocation if necessary
    int64_t calculateConstantOperand(SExpression & expr, uint64_t address, uint32_t fieldSize); // calculate constant or immediate operand possibly involving symbol. generate relocation if necessary
    void makeBinaryCode();                       // make binary data for code sections
    void makeBinaryData();                       // make binary data for data sections
    void makeBinaryRelocations();                // put relocation records in output file
    void showTokens();                           // Show all tokens. For debugging only
    void showSymbols();                          // Show all symbols. For debugging only
    void interpretHighLevelStatement();          // if, else, switch, for, do, while statements    
    void interpretEndBracket();                  // finish {} block
    void codeIf();                               // Interpret if statement in assembly code
    void codeIf2();                              // Finish if statement at end bracket
    void codeWhile();                            // Interpret while loop in assembly code
    void codeWhile2();                           // Finish while-loop at end bracket
    void codeDo();                               // Interpret do-while loop in assembly code
    void codeDo2();                              // Finish do-while loop at end bracket
    void codeFor();                              // Interpret for-loop in assembly code
    void codeFor2();                             // Finish for-loop at end bracket
    void codeForIn();                            // Interpret for-in vector loop in assembly code
    void codeForIn2();                           // Finish for-in vector loop in assembly code
    void codeSwitch();                           // Interpret switch statement in assembly code
    void codeCase();                             // Interpret switch case label in assembly code
    void codeSwitch2();                          // Finish switch statement at end bracket
    void codeBreak();                            // Interpret break or continue statement in assembly code
    uint32_t findBreakTarget(uint32_t k);        // Find or make the target symbol of a break or continue statement
    uint32_t makeLabelSymbol(const char * name); // Make a symbol for branch label etc., address not known yet
    bool mergeJump(SCode & code2);               // Merge jump instruction with preceding arithmetic instruction
    uint32_t hasJump(uint32_t line);             // check if line contains unconditional direct jump
    void interpretCondition(SCode & code);       // interpret condition in if(), while(), and for(;;) statements
    void codePush();                             // push register on stack. (may be replaced by macros later)
    void codePop();                              // pop register from stack. (may be replaced by macros later)
};
 

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.