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

Subversion Repositories forwardcom

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /forwardcom
    from Rev 45 to Rev 46
    Reverse comparison

Rev 45 → Rev 46

/bintools/assem6.cpp
0,0 → 1,1019
/**************************** assem6.cpp ********************************
* Author: Agner Fog
* Date created: 2017-08-07
* Last modified: 2021-02-23
* Version: 1.11
* Project: Binary tools for ForwardCom instruction set
* Module: assem.cpp
* Description:
* Module for assembling ForwardCom .as files.
* This module contains:
* - pass4(): Resolve internal cross references, optimize forward references
* - pass5(): Make binary file
* Copyright 2017-2021 GNU General Public License http://www.gnu.org/licenses
******************************************************************************/
#include "stdafx.h"
 
 
// Resolve symbol addresses and internal cross references, optimize forward references
void CAssembler::pass4() {
uint32_t addr = 0; // address relative to current section begin
//uint32_t instructId; // instruction id
uint32_t i; // loop counter
uint32_t symi; // symbol index
uint32_t numUncertain; // number of instructions with unresolved size in current section
uint32_t totUncertain; // number of instructions with unresolved size in all sections
uint32_t changes = 1; // number of size changes during each optimization pass
uint32_t optiPass = 0; // count optimization passes
uint32_t nSections = sectionHeaders.numEntries(); // number of sections
uint32_t const maxOptiPass = 10; // maximum number of optimization passes
 
// multiple optimization passes until size is certain or no changes
for (optiPass = 1; optiPass <= maxOptiPass; optiPass++) {
code_size = cmd.codeSizeOption; // initialize options in case they have been changed during pass 3 or 4
data_size = cmd.dataSizeOption;
 
if (changes == 0 && (totUncertain == 0 || optiPass > 2)) break;
changes = 0; // count instructions with changed size
section = 0;
numUncertain = totUncertain = 0;
for (i = 1; i < nSections; i++) {
sectionHeaders[i].sh_link = 0; // reset count of uncertain instruction sizes
sectionHeaders[i].sh_size = 0;
}
// loop through code objects
for (i = 0; i < codeBuffer.numEntries(); i++) {
//instructId = codeBuffer[i].instr1;
if (codeBuffer[i].section == 0 || codeBuffer[i].section >= nSections)
continue;
if (codeBuffer[i].section != section) {
if (section) {
// save results of previous section
sectionHeaders[section].sh_size = addr;
sectionHeaders[section].sh_link = numUncertain; // sh_link is temporarily used for indicating number of instructions with uncertain size
totUncertain += numUncertain;
}
// restore status for current section
section = codeBuffer[i].section;
addr = (uint32_t)sectionHeaders[section].sh_size;
numUncertain = sectionHeaders[section].sh_link;
}
codeBuffer[i].address = addr;
if (codeBuffer[i].label) {
// there is a label here. put the address into the symbol record
symi = findSymbol(codeBuffer[i].label);
if (symi > 0 && symi < symbols.numEntries()) {
// the upper half of st_value is temporarily used for indicating if address is not yet precise
symbols[symi].st_value = addr | (uint64_t)numUncertain << 32;
symbols[symi].st_unitsize = 1; // set an arbitrary size to indicate that a value has been assigned
}
}
if (codeBuffer[i].sizeUnknown) {
// update the size of this instruction
uint8_t lastSize = codeBuffer[i].size;
if (codeBuffer[i].instr1) { // update normal instruction
if (optiPass >= maxOptiPass - 1) {
// rare case. optimization has slow convergence. choose larger instruction size if uncertain
if (codeBuffer[i].fitAddr) codeBuffer[i].fitAddr |= IFIT_LARGE;
if (codeBuffer[i].fitJump) codeBuffer[i].fitJump |= IFIT_LARGE;
}
sectionHeaders[section].sh_link = numUncertain;
fitConstant(codeBuffer[i]); // recalculate necessary size of immediate constant
fitAddress(codeBuffer[i]); // recalculate necessary size of address
fitCode(codeBuffer[i]); // fit instruction to new size
if (codeBuffer[i].size != lastSize) changes++; // count changes if size changed
}
else { // not an instruction
if (codeBuffer[i].instruction == II_ALIGN) {
// align directive. round up address to nearest multiple of alignment value
uint32_t ali = bitScanReverse(codeBuffer[i].value.u);
uint32_t newAddress = (addr + ali - 1) & uint32_t(-(int32_t)ali);
codeBuffer[i].size = (newAddress - addr) >> 2; // size of alignment fillers
if (codeBuffer[i].size != lastSize) changes++; // count changes if size changed
if (numUncertain) numUncertain += (ali >> 2) - 1 - codeBuffer[i].size; // maximum additional size if size of previous instructions change
if (section && sectionHeaders[section].sh_align < ali) {
sectionHeaders[section].sh_align = ali; // adjust alignment of this section
}
}
else if (codeBuffer[i].instruction == II_OPTIONS) {
// options directive. change option
// This code object was created by CAssembler::interpretOptionsLine()
switch (codeBuffer[i].fitNum) {
case 1:
code_size = codeBuffer[i].value.u;
break;
case 2:
data_size = codeBuffer[i].value.u;
break;
}
}
}
}
addr += codeBuffer[i].size * 4; // update address
numUncertain += codeBuffer[i].sizeUnknown & 0x7F; // update uncertainty
}
// update last section
if (section) {
// save results of previous section
sectionHeaders[section].sh_size = addr;
sectionHeaders[section].sh_link = numUncertain;
totUncertain += numUncertain;
}
}
// remove temporary uncertainty information from symbol records
for (symi = 1; symi < symbols.numEntries(); symi++) {
if (symbols[symi].st_type == STT_OBJECT || symbols[symi].st_type == STT_FUNC) {
symbols[symi].st_value &= 0xFFFFFFFFU;
}
}
 
// make public symbol definitions
for (linei = 1; linei < lines.numEntries(); linei++) {
if (lines[linei].type == LINE_PUBLICDEF) {
interpretPublicDirective();
}
}
}
 
 
// interpret public name: options {, name: options}
void CAssembler::interpretPublicDirective() {
int state = 0; // 0: start
// 1: after 'public' or ','
// 2: after name
// 3: after ':'
// 4: after attribute
 
uint32_t symi = 0; // symbol index
uint32_t symn; // symbol name index
uint32_t tok; // token index
uint32_t symtok = 0; // symbol token
SToken token; // current token
 
tokenB = lines[linei].firstToken; // first token in line
tokenN = lines[linei].numTokens; // number of tokens in line
// loop through tokens on this line
for (tok = tokenB; tok < tokenB + tokenN; tok++) {
token = tokens[tok];
switch (state) {
case 0: // start
if (token.id == DIR_PUBLIC) state = 1; else return;
break;
case 1: // expect symbol name
if (token.type == TOK_SYM) {
symtok = tok;
symn = token.id;
symi = findSymbol(symn);
if ((int32_t)symi < 1) {
errors.report(token.pos, token.stringLength, ERR_SYMBOL_UNDEFINED); return;
}
state = 2;
}
else if (token.type == TOK_NAM) {
// name found. find symbol
symi = findSymbol((char*)buf() + tokens[tok].pos, tokens[tok].stringLength);
if ((int32_t)symi < 1) {
errors.report(token.pos, token.stringLength, ERR_SYMBOL_UNDEFINED); return;
}
symtok = tok;
symn = symbols[symi].st_name;
state = 2;
}
else errors.report(token);
break;
case 2: // after name. expect ':' or ','
if (token.type == TOK_OPR && token.id == ':') state = 3;
else if (token.type == TOK_OPR && token.id == ',') {
EXPORT_SYMBOL:
// check if external
if (symbols[symi].st_section == 0) {
errors.report(tokens[symtok].pos, tokens[symtok].stringLength, ERR_CANNOT_EXPORT);
state = 1;
continue;
}
// check symbol type
switch (symbols[symi].st_type) {
case STT_NOTYPE: // type missing. set type
symbols[symi].st_type = (symbols[symi].st_other & STV_EXEC) ? STT_FUNC : STT_OBJECT;
break;
case STT_OBJECT:
case STT_FUNC:
break; // ok
case STT_CONSTANT:
if (sectionHeaders.numEntries() == 0) {
// file must have at least one section because constant needs a section idex
err.submit(ERR_ELF_NO_SECTIONS);
}
break; // ok
case STT_VARIABLE: // meta-variable has been assigned multiple values
errors.report(tokens[symtok].pos, tokens[symtok].stringLength, ERR_SYMBOL_REDEFINED);
state = 1;
continue;
case STT_EXPRESSION: // cannot export expression
errors.report(tokens[symtok].pos, tokens[symtok].stringLength, ERR_EXPORT_EXPRESSION);
state = 1;
continue;
default:
errors.report(tokens[symtok].pos, tokens[symtok].stringLength, ERR_CANNOT_EXPORT);
state = 1;
continue;
}
// make symbol global or weak
if (symbols[symi].st_bind != STB_WEAK) symbols[symi].st_bind = STB_GLOBAL;
state = 1;
}
else {
errors.report(token); return;
}
break;
case 3: // after ':'. expect attribute
SET_ATTRIBUTE:
if (token.id == ATT_WEAK) {
symbols[symi].st_bind = STB_WEAK;
}
else if (token.id == ATT_CONSTANT && symbols[symi].st_type != STT_OBJECT && symbols[symi].st_type != STT_FUNC) {
symbols[symi].st_type = STT_CONSTANT;
}
else if (token.id == DIR_FUNCTION) {
symbols[symi].st_type = STT_FUNC;
}
else if (token.id == REG_IP) {
symbols[symi].st_other = (symbols[symi].st_other & ~ (SHF_DATAP | SHF_THREADP)) | STV_IP;
}
else if (token.id == REG_DATAP) {
symbols[symi].st_other = (symbols[symi].st_other & ~ (STV_IP | SHF_THREADP)) | SHF_DATAP;
}
else if (token.id == REG_THREADP) {
symbols[symi].st_other = (symbols[symi].st_other & ~ (STV_IP | SHF_DATAP)) | SHF_THREADP;
}
else if (token.id == ATT_REGUSE) {
if (tokens[tok + 1].id == '=' && tokens[tok + 2].type == TOK_NUM) {
tok += 2;
symbols[symi].st_reguse1 = expression(tok, 1, 0).value.w;
symbols[symi].st_other |= STV_REGUSE;
if (tokens[tok + 1].id == ',' && tokens[tok + 2].type == TOK_NUM) {
tok += 2;
symbols[symi].st_reguse2 = expression(tok, 1, 0).value.w;
}
}
}
else errors.report(token);
state = 4;
break;
case 4: // after attribute. expect ',' or more attributes
if (token.type == TOK_OPR && token.id == ',') {
uint32_t typ2 = tokens[tok+1].type;
if (typ2 == TOK_ATT || typ2 == TOK_DIR || typ2 == TOK_REG) break;
else goto EXPORT_SYMBOL;
}
if (token.type == TOK_ATT || token.type == TOK_DIR || token.type == TOK_REG)
goto SET_ATTRIBUTE;
errors.report(token);
return;
}
}
if (state > 1) goto EXPORT_SYMBOL; // unfinished symbol
}
 
 
// Make binary file
void CAssembler::pass5() {
 
// make a databuffer for each section
uint32_t nSections = sectionHeaders.numEntries();
dataBuffers.setSize(nSections);
section = 0;
 
// make binary code from code records
makeBinaryCode();
 
// make binary data for data sections
makeBinaryData();
 
// make sections
copySections();
 
// copy symbols
copySymbols();
 
// copy relocations
makeBinaryRelocations();
 
// make output list file
if (cmd.outputListFile) makeListFile();
 
// remove local and external symbols if there is no relocation reference to them,
// and adjust relocation records with new symbol indexes, after making list file.
// Preserve local symbols if debugOptions > 0
outFile.removePrivateSymbols(cmd.debugOptions);
 
// write assembly output file
outFile.join(0); // make ELF file from sections, etc.
}
 
// copy sections to outFile
void CAssembler::copySections() {
for (uint32_t i = 1; i < sectionHeaders.numEntries(); i++) {
if (dataBuffers[i].dataSize() > sectionHeaders[i].sh_size) { // dataSize() is zero for uninitialized data sections
sectionHeaders[i].sh_size = dataBuffers[i].dataSize(); // this should never be necessary
}
sectionHeaders[i].sh_link = 0; // remove temporary information used during optimization passes
outFile.addSection(sectionHeaders[i], symbolNameBuffer, dataBuffers[i]);
}
}
 
// copy symbols to outFile
void CAssembler::copySymbols() {
for (uint32_t i = 0; i < symbols.numEntries(); i++) {
// exclude section symbols and local constants
if (symbols[i].st_type != STT_SECTION && symbols[i].st_type < STT_VARIABLE) {
// check if symbol is in a communal section
uint32_t sect = symbols[i].st_section;
if (sect && sect < sectionHeaders.numEntries() && sectionHeaders[sect].sh_type == SHT_COMDAT && symbols[i].st_bind == STB_GLOBAL) {
// public symbol in communal section must be weak
symbols[i].st_bind = STB_WEAK;
}
uint32_t newSymi = outFile.addSymbol(symbols[i], symbolNameBuffer);
// save new symbol index for use in relocation records
symbols[i].st_unitnum = newSymi;
}
}
}
 
// make binary data for code sections
void CAssembler::makeBinaryCode() {
uint32_t i; // loop counter
STemplate instr; // instruction template
uint32_t format; // format
uint32_t templ; // format template
uint32_t instructId; // instruction as index into instructionlistId
SFormat const * formatp = 0; // record in formatList
uint32_t nSections = sectionHeaders.numEntries();
 
// loop through code objects
for (i = 0; i < codeBuffer.numEntries(); i++) {
instructId = codeBuffer[i].instr1;
if (instructId == 0) {
// not an instruction. possibly label or directive
if (codeBuffer[i].instruction == II_ALIGN && section) {
// alignment directive. size has been calculated in pass 4
int32_t asize = codeBuffer[i].size;
instr.q = 0; // nop instruction
if (asize & 1) {
dataBuffers[section].push(&instr, 4); // single size nop
asize -= 1;
}
instr.a.il = 2; // double size nop
while (asize >= 2) {
dataBuffers[section].push(&instr, 8); // add double size nop
asize -= 2;
}
}
// else if (codeBuffer[i].instruction == II_OPTIONS) {} // II_OPTIONS can be ignored here
continue; // skip the rest
}
section = codeBuffer[i].section;
if (section == 0 || section >= nSections) continue;
 
instr.q = 0; // reset template
formatp = codeBuffer[i].formatp;
templ = formatp->tmplate;
format = formatp->format2;
 
// assign registers
uint8_t opAvail = formatp->opAvail; // registers available in this format
 
int nOp = instructionlistId[instructId].sourceoperands;
if (nOp > 3 && instructionlistId[instructId].opimmediate) {
opAvail |= 1; // 3 registers and an immediate currently used only in truth_tab3 instruction
}
 
if (templ == 0xA || templ == 0xE) nOp++; // make one more register for fallback, even if it is unused
 
uint8_t operands[4] = {0,0,0,0};
int a = 0; // bit index to opAvail
int j = 3; // Index into operands
// Loop through the bits in opAvail in reverse order to pick operands according to priority
while (j >= 0 && a < 8) {
if (opAvail & (1 << a)) {
operands[j--] = 1 << a;
}
a++;
}
 
// List register operands
uint8_t registers[4] = {0,0,0,0};
a = 3;
if (codeBuffer[i].etype & XPR_REG3) registers[a--] = codeBuffer[i].reg3;
if (codeBuffer[i].etype & XPR_REG2) registers[a--] = codeBuffer[i].reg2;
if (codeBuffer[i].etype & XPR_REG1) registers[a--] = codeBuffer[i].reg1;
// Make any remaining registers equal to fallback or first source
// to avoid false dependence on unused register in superscalar processor
while (a >= 0) {
if (codeBuffer[i].etype & (XPR_MASK | XPR_FALLBACK)) {
registers[a--] = codeBuffer[i].fallback;
}
else {
registers[a--] = codeBuffer[i].reg1;
}
}
 
// Loop through operands to assign registers
for (j = 3, a = 3; j >= 0; j--) {
// put next operand in the sequence reg3, reg2, reg1, fallback into rt, rs, ru, or rd
// these may be overwritten below in template B, C, and D.
switch (operands[j]) {
case 0x10: // rt
instr.a.rt = registers[a--] & 0x1F;
break;
case 0x20: // rs
instr.a.rs = registers[a--] & 0x1F;
break;
case 0x40: // ru
instr.a.ru = registers[a--] & 0x1F;
break;
case 0x80: // rd
instr.a.rd = registers[a--] & 0x1F;
break;
default:; // memory and immediate operands or nothing
}
}
 
// insert other fields
instr.a.il = (format >> 8) & 3; // il = instruction length
instr.a.mode = (format >> 4) & 7; // mode
instr.a.op1 = instructionlistId[instructId].op1; // operation
if (templ != 0xD) {
if (codeBuffer[i].dest != 2 && codeBuffer[i].dest != 0) instr.a.rd = codeBuffer[i].dest & 0x1F; // destination register
if (templ != 0xC) {
instr.a.ot = codeBuffer[i].dtype & 7; // operand type
if (format & 0x80) instr.a.ot |= 4; // M bit
if (templ != 0xB) {
if (codeBuffer[i].etype & XPR_MASK) {
instr.a.mask = codeBuffer[i].mask; // mask register
}
else {
instr.a.mask = 7; // no mask
}
}
}
}
 
uint8_t * instr_b = instr.b; // avoid pedantic warnings from Gnu compiler
// memory operand
if (formatp->mem) {
if (formatp->mem & 2) instr.a.rs = codeBuffer[i].base & 0x1F; // base in rs
if (formatp->mem & 4) instr.a.rt = codeBuffer[i].index & 0x1F; // index in rt
uint8_t oldBase = codeBuffer[i].base; // save base pointer
 
// calculate offset, possibly involving symbols. make relocation if necessary
int64_t offset = calculateMemoryOffset(codeBuffer[i]);
 
if (codeBuffer[i].base != oldBase) {
// base pointer changed by calculateMemoryOffset
switch (codeBuffer[i].formatp->mem & 3) {
case 1: // base in RT. obsolete
instr.a.rt = codeBuffer[i].base; break;
case 2: // base in RS
instr.a.rs = codeBuffer[i].base; break;
}
}
 
// insert limit
if (codeBuffer[i].etype & XPR_LIMIT) offset = codeBuffer[i].value.i;
 
uint32_t addrPos = formatp->addrPos; // position of offset field
switch (formatp->addrSize) { // size of offset
case 0: // no offset
break;
case 1: // 8 bits offset
instr.b[addrPos] = uint8_t(offset);
break;
case 2: // 16 bits offset
*(int16_t *)(instr_b + addrPos) = int16_t(offset);
break;
case 4: // 32 bits offset
*(int32_t *)(instr_b + addrPos) = int32_t(offset);
break;
case 8: // 64 bits offset
*(int64_t *)(instr_b + addrPos) = offset;
}
// memory length or broadcast
if (formatp->vect & 6) instr.a.rt = codeBuffer[i].length;
}
 
// jump offset
if (formatp->jumpSize) {
 
// calculate offset, possibly involving symbols. make relocation if necessary
int64_t offset = calculateJumpOffset(codeBuffer[i]);
 
uint32_t addrSize = formatp->jumpSize; // size of offset field
uint32_t addrPos = formatp->jumpPos; // position of offset field
 
switch (addrSize) { // size of offset
case 0: // no offset
break;
case 1: // 8 bits offset
instr.b[addrPos] = uint8_t(offset);
break;
case 2: // 16 bits offset
*(int16_t *)(instr_b + addrPos) = int16_t(offset);
break;
case 3: // 24 bits offset
*(int16_t *)(instr_b + addrPos) = int16_t(offset); // first 16 of 24 bits
*(int8_t *)(instr_b + addrPos + 2) = int8_t(offset >> 16); // last 8 bits
break;
case 4: // 32 bits offset
*(int32_t *)(instr_b + addrPos) = int32_t(offset);
break;
case 8: // 64 bits offset
*(int64_t *)(instr_b + addrPos) = offset;
}
}
 
// immediate operand
if (formatp->immSize) {
int64_t value = codeBuffer[i].value.i; // value of operand
if (codeBuffer[i].sym3) {
// calculation of symbol address. add relocation if needed
value = calculateConstantOperand(codeBuffer[i], codeBuffer[i].address + codeBuffer[i].formatp->immPos, codeBuffer[i].formatp->immSize);
if (codeBuffer[i].etype & XPR_ERROR) {
linei = codeBuffer[i].line;
errors.reportLine(codeBuffer[i].value.w); // report error
}
}
 
uint32_t immPos = formatp->immPos; // position of immediate field
switch (formatp->immSize) { // size of immediate field
case 1: // 8 bits immediate
if ((codeBuffer[i].etype & XPR_IMMEDIATE) == XPR_FLT) {
*(int8_t *)(instr_b + immPos) = (int8_t)(int)(codeBuffer[i].value.d); // convert double to float16
}
else {
instr.b[immPos] = uint8_t(value);
}
break;
case 2: // 16 bits immediate
if (instructionlistId[instructId].opimmediate == OPI_INT1632 && format > 0x200) {
// 16-bit + 32 bit integer operands
*(int16_t *)(instr_b + immPos) = int16_t(value >> 32);
*(int32_t *)(instr_b + 4) = int32_t(value);
}
else if ((codeBuffer[i].etype & XPR_IMMEDIATE) == XPR_FLT) {
*(int16_t *)(instr_b + immPos) = double2half(codeBuffer[i].value.d); // convert double to float16
}
else {
*(int16_t *)(instr_b + immPos) = int16_t(value);
}
break;
case 4: // 32 bits immediate
if (instructionlistId[instructId].opimmediate == OPI_2INT16) {
// two 16-bit integer operands
value = (uint32_t)value << 16 | uint32_t(value >> 32);
*(int32_t *)(instr_b + immPos) = int32_t(value);
}
else if ((codeBuffer[i].etype & XPR_IMMEDIATE) == XPR_FLT) { // convert double to float
*(float *)(instr_b + immPos) = float(codeBuffer[i].value.d);
}
else {
*(int32_t *)(instr_b + immPos) = int32_t(value);
if (formatp->imm2 & 8) instr.a.im2 = uint16_t((uint64_t)value >> 32);
}
break;
case 8: // 64 bits immediate
if (instructionlistId[instructId].opimmediate == OPI_2INT32) {
// two 32-bit integers. swap them
value = value >> 32 | value << 32;
}
*(int64_t *)(instr_b + immPos) = value;
}
}
else if (opAvail & 1) { // special case: three registers and an immediate
int64_t value = calculateConstantOperand(codeBuffer[i], codeBuffer[i].address + codeBuffer[i].formatp->immPos, codeBuffer[i].formatp->immSize);
*(int16_t *)(instr_b + 4) = int16_t(value);
}
else if (formatp->tmplate == 0xC && instructionlistId[instructId].opimmediate == OPI_IMPLICIT) {
// insert implicit operand
instr.i[0] |= instructionlistId[instructId].implicit_imm;
}
if (formatp->imm2 & 0x80) { // various placements of OPJ
if (formatp->imm2 & 0x10) {
instr.b[7] = instructionlistId[instructId].op1; // OPJ in high part of IM2
}
else if (formatp->imm2 & 0x40) { // no OPJ
}
else {
instr.b[0] = instructionlistId[instructId].op1; // OPJ is in IM1
}
instr.a.op1 = format & 7; // op1 is part of format
}
if (formatp->imm2 & 0x40) {
// insert constant
if (formatp->format2 == 0x155) {
instr.i[0] = fillerInstruction; // filler instruction
}
}
// additional fields for format E
if (templ == 0xE) {
instr.a.mode2 = format & 7;
if (formatp->imm2 & 2) instr.a.im3 = codeBuffer[i].optionbits;
if (!(formatp->imm2 & 0x100))
instr.a.op2 = instructionlistId[instructId].op2;
}
 
if (formatp->category == 3 && instr.a.op1 == 0 && instr.a.op2 == 0) {
// simplify NOP instruction. Remove all unnecessary bits
instr.a.mask = 0;
instr.a.ot = 0;
if (instr.a.il > 1) instr.i[1] = 0;
}
 
// save code
uint32_t ilen = instr.a.il;
if (ilen == 0) ilen = 1;
dataBuffers[section].push(&instr, ilen * 4);
}
}
 
// make binary data for data sections
void CAssembler::makeBinaryData() {
// similar to pass2, but data lines only
section = 0;
 
// lines loop
for (linei = 1; linei < lines.numEntries(); linei++) {
tokenB = lines[linei].firstToken; // first token in line
tokenN = lines[linei].numTokens; // number of tokens in line
if (lines[linei].type == LINE_SECTION && tokens[tokenB+1].type == TOK_DIR) {
switch (tokens[tokenB+1].id) {
case DIR_SECTION: // section starts here
interpretSectionDirective();
break;
case DIR_END: // section or function end
interpretEndDirective();
break;
default:
errors.report(tokens[tokenB + 1]);
}
}
else if (lines[linei].type == LINE_DATADEF) {
lineError = 0;
tokenB = lines[linei].firstToken; // first token in line
tokenN = lines[linei].numTokens; // number of tokens in line
if (tokens[tokenB].type == TOK_DIR) continue; // ignore directives here
if (tokenN > 1) { // lines with a single token cannot legally define a symbol name
if (tokens[tokenB].type == TOK_TYP && tokens[tokenB+1].type == TOK_SYM) {
interpretVariableDefinition2();
}
else if (tokens[tokenB].type == TOK_ATT && tokens[tokenB].id == ATT_ALIGN) {
interpretAlign();
}
else {
interpretVariableDefinition1();
}
}
}
}
}
 
 
// put relocation records in output file
void CAssembler::makeBinaryRelocations() {
uint32_t i; // loop counter
// copy relocation records
for (i = 0; i < relocations.numEntries(); i++) {
// translate symbol indexes in relocation records
int32_t symi1, symi2; // symbol index
uint32_t newSymi1, newSymi2; // symbol index in output file
if (relocations[i].r_sym) {
symi1 = findSymbol(relocations[i].r_sym);
if (symi1 > 0) {
newSymi1 = symbols[symi1].st_unitnum;
relocations[i].r_sym = newSymi1; // replace by symbol index in outFile
uint32_t sect = symbols[symi1].st_section;
if (sect && symbols[symi1].st_bind == STB_WEAK) {
// there is a local reference to a weak public symbol. Make it both import and export
outFile.symbols[newSymi1].st_bind = STB_WEAK2;
}
if (sect && sect < sectionHeaders.numEntries() && sectionHeaders[sect].sh_type == SHT_COMDAT) {
// there is a local reference to a symbol in a communal section. Make it both import and export
outFile.symbols[newSymi1].st_bind = STB_WEAK2;
}
}
else relocations[i].r_sym = 0; // should not occur
}
if (relocations[i].r_refsym) { // reference symbol
symi2 = findSymbol(relocations[i].r_refsym);
if (symi2 > 0) {
newSymi2 = symbols[symi2].st_unitnum;
relocations[i].r_refsym = newSymi2; // replace by symbol index in outFile
if (symbols[symi2].st_section && symbols[symi2].st_bind == STB_WEAK) {
// there is a local reference to a weak public symbol. Make it both import and export
outFile.symbols[newSymi2].st_bind = STB_WEAK2;
}
}
else relocations[i].r_refsym = 0; // should not occur
}
outFile.addRelocation(relocations[i]); // put relocation in outFile
}
}
 
// make output listing
void CAssembler::makeListFile() {
// Use the disassembler for making output listing
CDisassembler disassembler; // make an instance of CDisassembler
// give all my tables to the disassembler
disassembler.getComponents2(outFile, instructionlist);
// change output file name
disassembler.outputFile = cmd.outputListFile;
// do the disassembly
disassembler.go();
}
 
// calculate memory address possibly involving symbol. generate relocation if necessary
int64_t CAssembler::calculateMemoryOffset(SCode & code) {
int64_t value = 0;
int32_t symi1 = 0, symi2 = 0;
if (code.sym1) symi1 = findSymbol(code.sym1); // target symbol, if any
if (code.sym2) symi2 = findSymbol(code.sym2); // reference symbol, if any
ElfFwcReloc relocation; // relocation, if needed
bool needsRelocation = false; // relocation needed
 
uint8_t fieldPos = code.formatp->addrPos; // position of address or immediate field
uint8_t fieldSize = code.formatp->addrSize; // size of address or immediate field
 
uint32_t scale = 0; // log2 scale factor to address, not including explicit symbol scale
if (fieldSize == 1) {
// scale factor determined by type
uint32_t type = code.dtype;
scale = type & 0xF;
if (type & 0x40) scale -= 3;
}
 
// check target symbol
if (symi1) {
if (symi2) {
// difference between two symbols
if (code.symscale1 == 0) code.symscale1 = 1;
if (symbols[symi1].st_section == symbols[symi2].st_section && symbols[symi1].st_bind == STB_LOCAL && symbols[symi2].st_bind == STB_LOCAL) {
// both symbols are local in same section. final value can be calculated
value = (int64_t)(symbols[symi1].st_value - symbols[symi2].st_value) / code.symscale1;
value = (value + code.offset_mem) >> scale;
}
else {
// symbols are in different section or external. relocation needed
relocation.r_type = R_FORW_REFP; // relative to arbitrary reference point
relocation.r_type |= bitScanReverse(code.symscale1) + scale; // scale factor
relocation.r_sym = code.sym1; // Symbol index
relocation.r_refsym = code.sym2; // Reference symbol
relocation.r_addend = uint32_t(code.offset_mem); // Addend
needsRelocation = true;
}
}
else {
// a single symbol
// is symbol relative to IP, DATAP, THREADP or constant?
//uint8_t basepointer = 0;
uint32_t symsection = symbols[symi1].st_section;
if (symbols[symi1].st_type == STT_CONSTANT) {
// constant
relocation.r_type = R_FORW_ABS | scale;
relocation.r_sym = code.sym1; // Symbol index
relocation.r_refsym = 0; // Reference symbol
relocation.r_addend = uint32_t(code.offset_mem); // Addend
needsRelocation = true;
}
else if (symsection > 0 && symsection < sectionHeaders.numEntries()) {
// local symbol relative to IP or DATAP
if (sectionHeaders[symsection].sh_flags & (SHF_IP | SHF_EXEC)) {
if (symsection == section) {
// symbol in same section relative to IP. calculate address
code.base = uint8_t(REG_IP >> 16);
value = (int64_t)(symbols[symi1].st_value - uint64_t(code.address + code.size * 4));
value = (value + code.offset_mem) >> scale; // scale offset
}
else {
// local symbol in different IP section. needs relocation
code.base = uint8_t(REG_IP >> 16);
relocation.r_type = R_FORW_SELFREL; // self-relative
//if (code.instruction & II_JUMP_INSTR) relocation.r_type |= R_FORW_SCALE4; // jump instruction scaled by 4
relocation.r_addend = fieldPos - code.size * 4; // position of relocated field relative to instruction end
relocation.r_sym = code.sym1; // temporary symbol index. resolve when symbol table created
relocation.r_refsym = 0;
relocation.r_addend += (int32_t)code.offset_mem;
needsRelocation = true;
}
}
else {
// relative to DATAP or THREADP. needs relocation
if (sectionHeaders[symsection].sh_flags & SHF_THREADP) {
code.base = uint8_t(REG_THREADP >> 16);
relocation.r_type = R_FORW_THREADP; // relocation relative to THREADP
}
else {
code.base = uint8_t(REG_DATAP >> 16);
relocation.r_type = R_FORW_DATAP; // relocation relative to DATAP
}
relocation.r_type |= scale; // scale factor only if 8-bit offset allowed
relocation.r_sym = code.sym1; // temporary symbol index. resolve when symbol table created
relocation.r_refsym = 0;
relocation.r_addend = uint32_t(code.offset_mem);
needsRelocation = true;
}
}
else {
// remote symbol relative to IP or DATAP
if (symbols[symi1].st_other & (STV_IP | STV_EXEC)) {
// relative to IP
code.base = uint8_t(REG_IP >> 16);
relocation.r_type = R_FORW_SELFREL;
//if (code.instruction & II_JUMP_INSTR) relocation.r_type |= R_FORW_SCALE4;
relocation.r_addend = fieldPos - code.size * 4; // position of relocated field relative to instruction end
}
else if (symbols[symi1].st_other & STV_THREADP) {
// relative to THREADP
code.base = uint8_t(REG_THREADP >> 16);
relocation.r_type = R_FORW_THREADP;
relocation.r_addend = 0;
}
else {
// relative to DATAP
code.base = uint8_t(REG_DATAP >> 16);
relocation.r_type = R_FORW_DATAP;
relocation.r_addend = 0;
}
relocation.r_sym = code.sym1; // temporary symbol index. resolve when symbol table created
relocation.r_refsym = 0;
relocation.r_addend += (int32_t)code.offset_mem;
if (code.formatp->addrSize == 1 && !(relocation.r_type & R_FORW_RELSCALEMASK)) {
relocation.r_type |= scale;
}
needsRelocation = true;
}
}
}
else {
// no symbol
value = code.offset_mem >> scale;
}
 
if (needsRelocation) {
// relocation needed. insert source address
relocation.r_type |= fieldSize << 8; // relocation size
relocation.r_offset = (uint64_t)code.address + fieldPos;
relocation.r_section = code.section;
value = 0; // value included in relocation addend
relocations.push(relocation); // save relocation
}
return value;
}
 
int64_t CAssembler::calculateJumpOffset(SCode & code) { // calculate jump offset possibly involving symbol. generate relocation if necessary
int64_t value = 0;
int32_t symi5 = 0;
if (code.sym5) symi5 = findSymbol(code.sym5); // target symbol, if any
ElfFwcReloc relocation; // relocation, if needed
bool needsRelocation = false; // relocation needed
 
uint8_t fieldSize = code.formatp->jumpSize; // size of jump offset field
uint8_t fieldPos = code.formatp->jumpPos; // position of jump offset field
uint32_t scale = 2; // jumps always scaled by 1 << 2 = 4
 
// check target symbol
if (symi5) {
uint32_t symsection = symbols[symi5].st_section;
 
if (symsection > 0 && symsection < sectionHeaders.numEntries()) {
// local symbol relative to IP
if (sectionHeaders[symsection].sh_flags & (SHF_IP | SHF_EXEC)) {
if (symsection == section) {
// symbol in same section relative to IP. calculate address
value = (int64_t)(symbols[symi5].st_value - uint64_t(code.address + code.size * 4));
value = (value + code.offset_jump) >> scale; // scale jump offset by 4
// address size must be at least 2
}
else {
// local symbol in different IP section. needs relocation
relocation.r_type = R_FORW_SELFREL; // self-relative
relocation.r_type |= R_FORW_SCALE4; // jump instruction scaled by 4
relocation.r_addend = fieldPos - code.size * 4; // position of relocated field relative to instruction end
relocation.r_sym = code.sym5; // temporary symbol index. resolve when symbol table created
relocation.r_refsym = 0;
relocation.r_addend += (int32_t)code.offset_jump;
needsRelocation = true;
}
}
}
else {
// remote symbol relative to IP
relocation.r_type = R_FORW_SELFREL;
relocation.r_type |= R_FORW_SCALE4;
relocation.r_addend = fieldPos - code.size * 4; // position of relocated field relative to instruction end
relocation.r_sym = code.sym5; // temporary symbol index. resolve when symbol table created
relocation.r_refsym = 0;
relocation.r_addend += (int32_t)code.offset_jump;
needsRelocation = true;
}
}
else {
// no symbol
value = code.offset_jump >> scale;
}
 
if (needsRelocation) {
// relocation needed. insert source address
relocation.r_type |= fieldSize << 8; // relocation size
relocation.r_offset = (uint64_t)code.address + fieldPos;
relocation.r_section = code.section;
value = 0; // value included in relocation addend
relocations.push(relocation); // save relocation
}
return value;
}
 
 
// calculate constant or immediate operand possibly involving symbol. generate relocation if necessary
int64_t CAssembler::calculateConstantOperand(SExpression & expr, uint64_t address, uint32_t fieldSize) {
int64_t value = 0;
int32_t symi3 = 0, symi4 = 0;
if (expr.sym3) {
symi3 = findSymbol(expr.sym3); // target symbol, if any
if (symi3 < 1) {errors.reportLine(ERR_SYMBOL_UNDEFINED); return 0;}
}
if (expr.sym4) {
symi4 = findSymbol(expr.sym4); // reference symbol, if any
if (symi4 < 1) {errors.reportLine(ERR_SYMBOL_UNDEFINED); return 0;}
}
 
ElfFwcReloc relocation; // relocation, if needed
bool needsRelocation = false;
// relocation needed
 
if (symi3) {
// there is a symbol
if (symi4) {
// difference between two symbols
if (symbols[symi3].st_section == symbols[symi4].st_section && symbols[symi3].st_bind == STB_LOCAL && symbols[symi4].st_bind == STB_LOCAL) {
// both symbols are local in same section. final value can be calculated
value = (int64_t)(symbols[symi3].st_value - symbols[symi4].st_value);
if (expr.symscale1 > 1) value /= expr.symscale1;
}
else {
// symbols are in different section or external. relocation needed
relocation.r_type = R_FORW_REFP; // relative to arbitrary reference point
if (expr.symscale1 > 1) relocation.r_type |= bitScanReverse(expr.symscale1); // scale factor
relocation.r_sym = expr.sym3; // Symbol index
relocation.r_refsym = expr.sym4; // Reference symbol
relocation.r_addend = int32_t(expr.value.w); // Addend
needsRelocation = true;
}
}
else {
// single symbol
if (symbols[symi3].st_type & STT_CONSTANT) {
// symbol is an external constant
relocation.r_type = R_FORW_ABS; // absolute value
if (expr.symscale1 > 1) relocation.r_type |= bitScanReverse(expr.symscale1); // scale factor
relocation.r_sym = expr.sym3; // Symbol index
relocation.r_refsym = 0; // Reference symbol
relocation.r_addend = int32_t(expr.value.w); // Addend
needsRelocation = true;
}
else if ((sectionHeaders[section].sh_flags & (SHF_WRITE | SHF_DATAP)) && fieldSize >= 4) {
// other symbol. absolute address allowed only in writeable data section
relocation.r_type = R_FORW_ABS; // absolute value, 64 bits, no scale
relocation.r_sym = expr.sym3; // Symbol index
relocation.r_refsym = 0; // Reference symbol
if (expr.symscale1 > 1) relocation.r_type |= bitScanReverse(expr.symscale1); // scale factor
relocation.r_addend = int32_t(expr.value.w); // Addend
if (symbols[symi3].st_section && fieldSize < 4) {
expr.etype = XPR_ERROR;
value = ERR_ABS_RELOCATION;
}
// warn if absolute address
err.submit(ERR_ABS_RELOCATION_WARN, lines[linei].linenum, (char*)symbolNameBuffer.buf() + symbols[symi3].st_name);
needsRelocation = true;
}
else {
// symbol without reference point not allowed here
expr.etype = XPR_ERROR;
value = ERR_ABS_RELOCATION;
}
}
}
else {
// no symbol
value = expr.value.i;
}
if (needsRelocation) {
// relocation needed. insert source address
relocation.r_offset = address;
relocation.r_section = section;
relocation.r_type |= fieldSize << 8; // relocation size
value = 0; // value included in relocation addend
relocations.push(relocation); // save relocation
}
return value;
}

powered by: WebSVN 2.1.0

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