URL
https://opencores.org/ocsvn/forwardcom/forwardcom/trunk
Subversion Repositories forwardcom
Compare Revisions
- This comparison shows the changes necessary to convert path
/forwardcom/bintools
- from Rev 45 to Rev 46
- ↔ Reverse comparison
Rev 45 → Rev 46
/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; |
} |