[Assembler] Implement segment changing directives
MODIFY
src/assembler/assembler.h
src/assembler/assembler.h
@@ -41,8 +41,158 @@ namespace AssemblerTmp {
} \
auto resName = std::get<resType>(operationFunction##_res);
-template <typename ISA>
class AssemblerBase {
+public:
+ std::optional<Error> setCurrentSegment(Segment seg) const {
+ if (m_segmentPointers.count(seg) == 0) {
+ return Error(0, "No base address set for segment '" + s_segmentName.at(seg) + +"'");
+ }
+ m_currentSegment = seg;
+ return {};
+ }
+
+ void setSegmentBase(Segment seg, uint32_t base) {
+ if (m_segmentPointers.count(seg) != 0) {
+ throw std::runtime_error("Base address already set for segment '" + s_segmentName.at(seg).toStdString() +
+ +"'");
+ }
+ m_segmentPointers[seg] = {base, base};
+ }
+
+protected:
+ void setDirectives(DirectiveVec& directives) {
+ if (m_directives.size() != 0) {
+ throw std::runtime_error("Directives already set");
+ }
+ m_directives = directives;
+ for (const auto& iter : m_directives) {
+ const auto directive = iter.get()->name();
+ if (m_directivesMap.count(directive) != 0) {
+ throw std::runtime_error("Error: directive " + directive.toStdString() +
+ " has already been registerred.");
+ }
+ m_directivesMap[directive] = iter;
+ }
+ }
+
+ virtual std::variant<Error, LineTokens> tokenize(const QString& line) const {
+ const static auto splitter = QRegularExpression(
+ R"(\t|\((?=x(?:[1-2]\d|3[0-1]|\d)|t[0-6]|a[0-7]|s(?:1[0-1]|\d)|[sgt]p|zero)|(?:x(?:[1-2]\d|3[0-1]|\d)|t[0-6]|a[0-7]|s(?:1[0-1]|\d)|[sgt]p|zero)\K\))");
+ return splitQuotes(line.split(splitter));
+ }
+
+ virtual HandleDirectiveRes assembleDirective(const TokenizedSrcLine& line, bool& ok) const {
+ if (line.tokens.empty()) {
+ return {Error(line.sourceLine, "Empty source lines should be impossible at this point")};
+ }
+
+ const auto& directive = line.tokens.at(0);
+ if (m_directivesMap.count(directive) == 0) {
+ ok = false;
+ // Not a directive
+ return std::nullopt;
+ }
+ ok = true;
+ return m_directivesMap.at(directive)->handle(this, line);
+ };
+ /**
+ * @brief splitSymbolsFromLine
+ * @returns a pair consisting of a symbol and the the input @p line tokens where the symbol has been removed.
+ */
+ virtual std::variant<Error, SymbolLinePair> splitSymbolsFromLine(const LineTokens& tokens, int sourceLine) const {
+ if (tokens.size() == 0) {
+ return std::pair<Symbols, LineTokens>({}, tokens);
+ }
+
+ LineTokens remainingTokens;
+ remainingTokens.reserve(tokens.size());
+ Symbols symbols;
+ bool symbolStillAllowed = true;
+ for (const auto& token : tokens) {
+ if (token.contains(':')) {
+ if (symbolStillAllowed) {
+ const QString cleanedSymbol = QString(token).remove(':');
+ if (symbols.count(cleanedSymbol) != 0) {
+ return Error(sourceLine, "Multiple definitions of symbol '" + cleanedSymbol + "'");
+ } else {
+ symbols.insert(cleanedSymbol);
+ }
+ } else {
+ return Error(sourceLine, "Stray ':' in line");
+ }
+ } else {
+ remainingTokens.append(token);
+ symbolStillAllowed = false;
+ }
+ }
+ return std::pair<Symbols, LineTokens>(symbols, remainingTokens);
+ }
+
+ virtual std::variant<Error, DirectivesLinePair> splitDirectivesFromLine(const LineTokens& tokens,
+ int sourceLine) const {
+ if (tokens.size() == 0) {
+ return std::pair<Directives, LineTokens>({}, tokens);
+ }
+
+ LineTokens remainingTokens;
+ remainingTokens.reserve(tokens.size());
+ Directives directives;
+ bool directivesStillAllowed = true;
+ for (const auto& token : tokens) {
+ if (token.startsWith('.')) {
+ if (directivesStillAllowed) {
+ directives.insert(token);
+ } else {
+ return Error(sourceLine, "Stray '.' in line");
+ }
+ } else {
+ remainingTokens.append(token);
+ directivesStillAllowed = false;
+ }
+ }
+ return std::pair<Directives, LineTokens>(directives, remainingTokens);
+ }
+
+ virtual std::variant<Error, LineTokens> splitCommentFromLine(const LineTokens& tokens, int sourceLine) const {
+ if (tokens.size() == 0) {
+ return {tokens};
+ }
+
+ LineTokens preCommentTokens;
+ preCommentTokens.reserve(tokens.size());
+ for (const auto& token : tokens) {
+ if (token.contains(commentDelimiter())) {
+ break;
+ } else {
+ preCommentTokens.append(token);
+ }
+ }
+ return {preCommentTokens};
+ }
+
+ virtual QChar commentDelimiter() const = 0;
+
+ /**
+ * @brief m_assemblerDirectives is the set of supported assembler directives.
+ */
+ DirectiveVec m_directives;
+ DirectiveMap m_directivesMap;
+
+ /**
+ * @brief m_segmentPointers maintains the current segment pointers for the segments annoted by the Segment enum
+ * class. The value is a pair containing <segment base, segment end>.
+ * Marked mutable to allow for modifying segment end pointer during assembling.
+ */
+ mutable std::map<Segment, std::pair<uint32_t, uint32_t>> m_segmentPointers;
+ /**
+ * @brief m_currentSegment maintains the current segment where the assembler emits information.
+ * Marked mutable to allow for switching currently selected segment during assembling.
+ */
+ mutable Segment m_currentSegment;
+};
+
+template <typename ISA>
+class Assembler : public AssemblerBase {
static_assert(std::is_base_of<ISAInfoBase, ISA>::value, "Provided ISA type must derive from ISAInfoBase");
public:
@@ -53,16 +203,6 @@ public:
using PseudoInstrMap = std::map<QString, std::shared_ptr<PseudoInstr>>;
using PseudoInstrVec = std::vector<std::shared_ptr<PseudoInstr>>;
-private:
- struct LinkRequest {
- unsigned sourceLine; // Source location of code which resulted in the link request
- uint32_t offset; // Offset of instruction which needs link resolution
-
- // Reference to the immediate field which resolves the symbol and the requested symbol
- FieldLinkRequest fieldRequest;
- };
- using LinkRequests = std::vector<LinkRequest>;
-
public:
AssembleResult assemble(const QString& program) const {
const auto programLines = program.split(QRegExp("[\r\n]"));
@@ -72,11 +212,14 @@ public:
AssembleResult assemble(const QStringList& programLines) const {
AssembleResult result;
+ // Per default, emit to .text until otherwise specified
+ setCurrentSegment(Segment::text);
+
// Tokenize each source line and separate symbol from remainder of tokens
- runPass(tokenizedLines, Program, pass0, programLines);
+ runPass(tokenizedLines, SourceProgram, pass0, programLines);
// Pseudo instruction expansion
- runPass(expandedLines, Program, pass1, tokenizedLines);
+ runPass(expandedLines, SourceProgram, pass1, tokenizedLines);
/** Assemble. During assembly, we generate:
* - symbolMap: Recoding the offset locations in the program of lines adorned with symbols
@@ -84,7 +227,7 @@ public:
*/
SymbolMap symbolMap;
LinkRequests needsLinkage;
- runPass(program, QByteArray, pass2, expandedLines, symbolMap, needsLinkage);
+ runPass(program, Program, pass2, expandedLines, symbolMap, needsLinkage);
// Symbol linkage
runPass(unused, NoPassResult, pass3, program, symbolMap, needsLinkage);
@@ -106,6 +249,7 @@ public:
auto& error = std::get<Error>(match);
// Unknown instruction
res.errors.push_back(error);
+ res.program << "unknown instruction";
} catch (const std::bad_variant_access&) {
// Got match, disassemble
auto tokens = std::get<const Instruction<ISA>*>(match)->disassemble(instructionWord, baseAddress + i,
@@ -125,14 +269,24 @@ public:
const Matcher<ISA>& getMatcher() { return *m_matcher; }
-private:
+protected:
+ struct LinkRequest {
+ unsigned sourceLine; // Source location of code which resulted in the link request
+ uint32_t offset; // Offset of instruction in segment which needs link resolution
+ Segment segment; // Segment which instruction was emitted in
+
+ // Reference to the immediate field which resolves the symbol and the requested symbol
+ FieldLinkRequest fieldRequest;
+ };
+ using LinkRequests = std::vector<LinkRequest>;
+
/**
* @brief pass0
* Line tokenization and source line recording
*/
- std::variant<Errors, Program> pass0(const QStringList& program) const {
+ std::variant<Errors, SourceProgram> pass0(const QStringList& program) const {
Errors errors;
- Program tokenizedLines;
+ SourceProgram tokenizedLines;
tokenizedLines.reserve(program.size());
/** @brief carry
@@ -179,9 +333,9 @@ private:
* @brief pass1
* Pseudo-op expansion. If @return errors is empty, pass succeeded.
*/
- std::variant<Errors, Program> pass1(const Program& tokenizedLines) const {
+ std::variant<Errors, SourceProgram> pass1(const SourceProgram& tokenizedLines) const {
Errors errors;
- Program expandedLines;
+ SourceProgram expandedLines;
expandedLines.reserve(tokenizedLines.size());
for (unsigned i = 0; i < tokenizedLines.size(); i++) {
@@ -221,37 +375,52 @@ private:
* In the following, current size of the program is used as an analog for the offset of the to-be-assembled
* instruction in the program. This is then used for symbol resolution.
*/
- std::variant<Errors, QByteArray> pass2(const Program& tokenizedLines, SymbolMap& symbolMap,
+ std::variant<Errors, Program> pass2(const SourceProgram& tokenizedLines, SymbolMap& symbolMap,
LinkRequests& needsLinkage) const {
- QByteArray program;
+ // Initialize program with initialized segments:
+ Program program;
+ for (const auto& iter : m_segmentPointers) {
+ program.segments[iter.first] = QByteArray();
+ }
+
Errors errors;
+ QByteArray* currentSegment = &program.segments.at(m_currentSegment);
+ uint32_t addr_offset = currentSegment->size();
+
+ bool wasDirective;
for (const auto& line : tokenizedLines) {
- const uint32_t instr_offset = program.size();
for (const auto& s : line.symbols) {
if (symbolMap.count(s) != 0) {
errors.push_back(Error(line.sourceLine, "Multiple definitions of symbol '" + s + "'"));
} else {
- symbolMap[s] = instr_offset;
+ symbolMap[s] = addr_offset;
}
}
- runOperation(directiveBytes, std::optional<QByteArray>, assembleDirective, line);
- if (!directiveBytes) {
+ runOperation(directiveBytes, std::optional<QByteArray>, assembleDirective, line, wasDirective);
+
+ // Currently emitting segment may have changed during the assembler directive; refresh state
+ currentSegment = &program.segments.at(m_currentSegment);
+ addr_offset = currentSegment->size();
+ if (!wasDirective) {
std::weak_ptr<Instr> assembledWith;
runOperation(machineCode, InstrRes, assembleInstruction, line, assembledWith);
if (!machineCode.linksWithSymbol.symbol.isEmpty()) {
LinkRequest req;
req.sourceLine = line.sourceLine;
- req.offset = instr_offset;
+ req.offset = addr_offset;
req.fieldRequest = machineCode.linksWithSymbol;
+ req.segment = m_currentSegment;
needsLinkage.push_back(req);
}
- program.append(
+ currentSegment->append(
QByteArray(reinterpret_cast<char*>(&machineCode.instruction), sizeof(machineCode.instruction)));
- } else {
- program.append(directiveBytes.value());
+ }
+ // This was a directive; check if any bytes needs to be appended to the segment
+ else if (directiveBytes) {
+ currentSegment->append(directiveBytes.value());
}
}
if (errors.size() != 0) {
@@ -261,7 +430,7 @@ private:
}
}
- std::variant<Errors, NoPassResult> pass3(QByteArray& program, const SymbolMap& symbolMap,
+ std::variant<Errors, NoPassResult> pass3(Program& program, const SymbolMap& symbolMap,
const LinkRequests& needsLinkage) const {
Errors errors;
for (const auto& linkRequest : needsLinkage) {
@@ -271,10 +440,12 @@ private:
continue;
}
+ QByteArray& segment = program.segments.at(linkRequest.segment);
+
// Decode instruction at link-request position
- assert(program.size() <= (linkRequest.offset + 4) &&
+ assert(segment.size() <= (linkRequest.offset + 4) &&
"Error: position of link request is not within program");
- uint32_t instr = *reinterpret_cast<uint32_t*>(program.data() + linkRequest.offset);
+ uint32_t instr = *reinterpret_cast<uint32_t*>(segment.data() + linkRequest.offset);
// Re-apply immediate resolution using the value acquired from the symbol map
if (auto* immField = dynamic_cast<const Imm*>(linkRequest.fieldRequest.field)) {
@@ -283,8 +454,8 @@ private:
assert(false && "Something other than an immediate field has requested linkage?");
}
- // Finally, overwrite the instruction in the program
- *reinterpret_cast<uint32_t*>(program.data() + linkRequest.offset) = instr;
+ // Finally, overwrite the instruction in the segment
+ *reinterpret_cast<uint32_t*>(segment.data() + linkRequest.offset) = instr;
}
if (errors.size() != 0) {
return {errors};
@@ -293,8 +464,31 @@ private:
}
}
-protected:
- AssemblerBase<ISA>() {}
+ virtual PseudoExpandRes expandPseudoOp(const TokenizedSrcLine& line) const {
+ if (line.tokens.empty()) {
+ return std::nullopt;
+ }
+ const auto& opcode = line.tokens.at(0);
+ if (m_pseudoInstructionMap.count(opcode) == 0) {
+ // Not a pseudo instruction
+ return std::nullopt;
+ }
+ return m_pseudoInstructionMap.at(opcode)->expand(line);
+ }
+
+ virtual AssembleRes assembleInstruction(const TokenizedSrcLine& line, std::weak_ptr<Instr>& assembledWith) const {
+ if (line.tokens.empty()) {
+ return {Error(line.sourceLine, "Empty source lines should be impossible at this point")};
+ }
+ const auto& opcode = line.tokens.at(0);
+ if (m_instructionMap.count(opcode) == 0) {
+ return {Error(line.sourceLine, "Unknown opcode '" + opcode + "'")};
+ }
+ assembledWith = m_instructionMap.at(opcode);
+ return m_instructionMap.at(opcode)->assemble(line);
+ };
+
+ Assembler<ISA>() {}
void initialize(InstrVec& instructions, PseudoInstrVec& pseudoinstructions, DirectiveVec& directives) {
setInstructions(instructions);
@@ -303,21 +497,6 @@ protected:
m_matcher = std::make_unique<Matcher<ISA>>(m_instructions);
}
- void setDirectives(DirectiveVec& directives) {
- if (m_directives.size() != 0) {
- throw std::runtime_error("Directives already set");
- }
- m_directives = directives;
- for (const auto& iter : m_directives) {
- const auto directive = iter.get()->name();
- if (m_directivesMap.count(directive) != 0) {
- throw std::runtime_error("Error: directive " + directive.toStdString() +
- " has already been registerred.");
- }
- m_directivesMap[directive] = iter;
- }
- }
-
void setPseudoInstructions(PseudoInstrVec& pseudoInstructions) {
if (m_pseudoInstructions.size() != 0) {
throw std::runtime_error("Pseudoinstructions already set");
@@ -349,157 +528,21 @@ protected:
}
}
- virtual PseudoExpandRes expandPseudoOp(const TokenizedSrcLine& line) const {
- if (line.tokens.empty()) {
- return std::optional<std::vector<AssemblerTmp::LineTokens>>{};
- }
- const auto& opcode = line.tokens.at(0);
- if (m_pseudoInstructionMap.count(opcode) == 0) {
- // Not a pseudo instruction
- return std::optional<std::vector<AssemblerTmp::LineTokens>>{};
- }
- return m_pseudoInstructionMap.at(opcode)->expand(line);
- }
-
- virtual std::variant<Error, LineTokens> tokenize(const QString& line) const {
- const static auto splitter = QRegularExpression(
- R"(\t|\((?=x(?:[1-2]\d|3[0-1]|\d)|t[0-6]|a[0-7]|s(?:1[0-1]|\d)|[sgt]p|zero)|(?:x(?:[1-2]\d|3[0-1]|\d)|t[0-6]|a[0-7]|s(?:1[0-1]|\d)|[sgt]p|zero)\K\))");
- return splitQuotes(line.split(splitter));
- }
-
- virtual AssembleRes assembleInstruction(const TokenizedSrcLine& line, std::weak_ptr<Instr>& assembledWith) const {
- if (line.tokens.empty()) {
- return {Error(line.sourceLine, "Empty source lines should be impossible at this point")};
- }
- const auto& opcode = line.tokens.at(0);
- if (m_instructionMap.count(opcode) == 0) {
- return {Error(line.sourceLine, "Unknown opcode '" + opcode + "'")};
- }
- assembledWith = m_instructionMap.at(opcode);
- return m_instructionMap.at(opcode)->assemble(line);
- };
-
- virtual HandleDirectiveRes assembleDirective(const TokenizedSrcLine& line) const {
- if (line.tokens.empty()) {
- return {Error(line.sourceLine, "Empty source lines should be impossible at this point")};
- }
-
- const auto& directive = line.tokens.at(0);
- if (m_directivesMap.count(directive) == 0) {
- // Not a directive
- return std::optional<QByteArray>();
- }
- return m_directivesMap.at(directive)->handle(line);
- };
/**
- * @brief splitSymbolsFromLine
- * @returns a pair consisting of a symbol and the the input @p line tokens where the symbol has been removed.
- */
- virtual std::variant<Error, SymbolLinePair> splitSymbolsFromLine(const LineTokens& tokens, int sourceLine) const {
- if (tokens.size() == 0) {
- return std::pair<Symbols, LineTokens>({}, tokens);
- }
-
- LineTokens remainingTokens;
- remainingTokens.reserve(tokens.size());
- Symbols symbols;
- bool symbolStillAllowed = true;
- for (const auto& token : tokens) {
- if (token.contains(':')) {
- if (symbolStillAllowed) {
- const QString cleanedSymbol = QString(token).remove(':');
- if (symbols.count(cleanedSymbol) != 0) {
- return Error(sourceLine, "Multiple definitions of symbol '" + cleanedSymbol + "'");
- } else {
- symbols.insert(cleanedSymbol);
- }
- } else {
- return Error(sourceLine, "Stray ':' in line");
- }
- } else {
- remainingTokens.append(token);
- symbolStillAllowed = false;
- }
- }
- return std::pair<Symbols, LineTokens>(symbols, remainingTokens);
- }
-
- virtual std::variant<Error, DirectivesLinePair> splitDirectivesFromLine(const LineTokens& tokens,
- int sourceLine) const {
- if (tokens.size() == 0) {
- return std::pair<Directives, LineTokens>({}, tokens);
- }
-
- LineTokens remainingTokens;
- remainingTokens.reserve(tokens.size());
- Directives directives;
- bool directivesStillAllowed = true;
- for (const auto& token : tokens) {
- if (token.startsWith('.')) {
- if (directivesStillAllowed) {
- directives.insert(token);
- } else {
- return Error(sourceLine, "Stray '.' in line");
- }
- } else {
- remainingTokens.append(token);
- directivesStillAllowed = false;
- }
- }
- return std::pair<Directives, LineTokens>(directives, remainingTokens);
- }
-
- virtual std::variant<Error, LineTokens> splitCommentFromLine(const LineTokens& tokens, int sourceLine) const {
- if (tokens.size() == 0) {
- return {tokens};
- }
-
- LineTokens preCommentTokens;
- preCommentTokens.reserve(tokens.size());
- for (const auto& token : tokens) {
- if (token.contains(commentDelimiter())) {
- break;
- } else {
- preCommentTokens.append(token);
- }
- }
- return {preCommentTokens};
- }
-
- virtual QString instrSegment() const { return QStringLiteral(".text"); }
- virtual QString dataSegment() const { return QStringLiteral(".data"); }
- virtual QChar commentDelimiter() const = 0;
-
- /**
- * @brief m_instructions is the set of instructions which can be matched from an instruction string as well as be
- * disassembled from a program.
+ * @brief m_instructions is the set of instructions which can be matched from an instruction string as well as
+ * be disassembled from a program.
*/
InstrVec m_instructions;
InstrMap m_instructionMap;
/**
- * @brief m_pseudoInstructions is the set of instructions which can be matched from an instruction string but cannot
- * be disassembled from a program. Typically, pseudoinstructions will expand to one or more non-pseudo instructions.
+ * @brief m_pseudoInstructions is the set of instructions which can be matched from an instruction string but
+ * cannot be disassembled from a program. Typically, pseudoinstructions will expand to one or more non-pseudo
+ * instructions.
*/
PseudoInstrVec m_pseudoInstructions;
PseudoInstrMap m_pseudoInstructionMap;
- /**
- * @brief m_assemblerDirectives is the set of supported assembler directives.
- */
- DirectiveVec m_directives;
- DirectiveMap m_directivesMap;
-
- /**
- * @brief m_segmentPointers maintains the current end-of-segment pointers for the segments annotated by the program
- * (ie. .text, .data, ...).
- */
- std::map<QString, uint32_t> m_segmentPointers;
- /**
- * @brief m_currentSegment maintains the current segment where the assembler emits information.
- */
- QString m_currentSegment;
-
std::unique_ptr<Matcher<ISA>> m_matcher;
};

MODIFY
src/assembler/assembler_defines.h
src/assembler/assembler_defines.h
@@ -28,16 +28,24 @@ struct TokenizedSrcLine {
uint32_t programAddress = -1;
};
using SymbolLinePair = std::pair<Symbols, LineTokens>;
-using Program = std::vector<TokenizedSrcLine>;
+using SourceProgram = std::vector<TokenizedSrcLine>;
using NoPassResult = std::monostate;
+enum class Segment { bss, data, text };
+const static std::map<Segment, QString> s_segmentName = {{Segment::bss, ".bss"},
+ {Segment::data, ".data"},
+ {Segment::text, ".text"}};
+struct Program {
+ std::map<Segment, QByteArray> segments;
+};
+
/**
* @brief The Result struct
* An assembly result is determined to be valid iff errors is empty.
*/
struct AssembleResult {
Errors errors;
- QByteArray program;
+ Program program;
};
struct DisassembleResult {

MODIFY
src/assembler/directive.h
src/assembler/directive.h
namespace Ripes {
namespace AssemblerTmp {
+class AssemblerBase;
+
class Directive {
public:
- using DirectiveHandler = std::function<HandleDirectiveRes(const Directive&, const TokenizedSrcLine&)>;
+ using DirectiveHandler = std::function<HandleDirectiveRes(const AssemblerBase*, const TokenizedSrcLine&)>;
Directive(const QString& directive, const DirectiveHandler& handler) : m_directive(directive), m_handler(handler) {}
- HandleDirectiveRes handle(const TokenizedSrcLine& line) { return m_handler(*this, line); }
+ HandleDirectiveRes handle(const AssemblerBase* assembler, const TokenizedSrcLine& line) {
+ return m_handler(assembler, line);
+ }
const QString& name() const { return m_directive; }
private:

MODIFY
src/assembler/gnudirectives.cpp
src/assembler/gnudirectives.cpp
#include "gnudirectives.h"
+#include "assembler.h"
namespace Ripes {
namespace AssemblerTmp {
@@ -43,6 +44,10 @@ DirectiveVec gnuDirectives() {
add_directive(directives, fourByteDirective());
add_directive(directives, longDirective());
+ add_directive(directives, dataDirective());
+ add_directive(directives, textDirective());
+ add_directive(directives, bssDirective());
+
return directives;
}
@@ -61,7 +66,7 @@ std::optional<Error> assembleData(const QStringList& tokens, QByteArray& byteArr
}
template <size_t width>
-HandleDirectiveRes dataFunctor(const Directive&, const TokenizedSrcLine& line) {
+HandleDirectiveRes dataFunctor(const AssemblerBase*, const TokenizedSrcLine& line) {
if (line.tokens.length() < 2) {
return {Error(line.sourceLine, "Invalid number of arguments")};
}
@@ -74,7 +79,7 @@ HandleDirectiveRes dataFunctor(const Directive&, const TokenizedSrcLine& line) {
}
};
-HandleDirectiveRes stringFunctor(const Directive&, const TokenizedSrcLine& line) {
+HandleDirectiveRes stringFunctor(const AssemblerBase*, const TokenizedSrcLine& line) {
if (line.tokens.length() != 2) {
return {Error(line.sourceLine, "Invalid number of arguments")};
}
@@ -128,8 +133,31 @@ Directive stringDirective() {
return Directive(".string", &stringFunctor);
}
+template <Segment seg>
+HandleDirectiveRes segmentChangeFunctor(const AssemblerBase* assembler, const TokenizedSrcLine& line) {
+ auto err = assembler->setCurrentSegment(seg);
+ if (err) {
+ // Embed source line into error message
+ err.value().first = line.sourceLine;
+ return {err.value()};
+ }
+ return std::nullopt;
+};
+
+Directive textDirective() {
+ return Directive(".text", &segmentChangeFunctor<Segment::text>);
+}
+
+Directive bssDirective() {
+ return Directive(".bss", &segmentChangeFunctor<Segment::bss>);
+}
+
+Directive dataDirective() {
+ return Directive(".data", &segmentChangeFunctor<Segment::data>);
+}
+
Directive zeroDirective() {
- auto zeroFunctor = [](const Directive&, const TokenizedSrcLine& line) -> HandleDirectiveRes {
+ auto zeroFunctor = [](const AssemblerBase*, const TokenizedSrcLine& line) -> HandleDirectiveRes {
if (line.tokens.length() != 2) {
return {Error(line.sourceLine, "Invalid number of arguments")};
}

MODIFY
src/assembler/gnudirectives.h
src/assembler/gnudirectives.h
@@ -26,5 +26,9 @@ Directive twoByteDirective();
Directive fourByteDirective();
Directive longDirective();
+Directive textDirective();
+Directive dataDirective();
+Directive bssDirective();
+
} // namespace AssemblerTmp
} // namespace Ripes

MODIFY
src/assembler/rv32i_assembler.cpp
src/assembler/rv32i_assembler.cpp
namespace Ripes {
namespace AssemblerTmp {
-RV32I_Assembler::RV32I_Assembler(const std::set<Extensions>& extensions) : AssemblerBase<ISAInfo<ISA::RV32IM>>() {
+RV32I_Assembler::RV32I_Assembler(const std::set<Extensions>& extensions) : Assembler<ISAInfo<ISA::RV32IM>>() {
auto [instrs, pseudos, directives] = initInstructions(extensions);
initialize(instrs, pseudos, directives);
// Initialize segment pointers
- m_segmentPointers[instrSegment()] = 0x0;
- m_segmentPointers[dataSegment()] = 0x10000000;
+ setSegmentBase(Segment::text, 0x0);
+ setSegmentBase(Segment::data, 0x10000000);
+ setSegmentBase(Segment::bss, 0x11000000);
}
std::tuple<RV32I_Assembler::RVInstrVec, RV32I_Assembler::RVPseudoInstrVec, DirectiveVec>

MODIFY
src/assembler/rv32i_assembler.h
src/assembler/rv32i_assembler.h
namespace Ripes {
namespace AssemblerTmp {
-class RV32I_Assembler : public AssemblerBase<ISAInfo<ISA::RV32IM>> {
+class RV32I_Assembler : public Assembler<ISAInfo<ISA::RV32IM>> {
private:
using RVInstr = Instruction<ISAInfo<ISA::RV32IM>>;
using RVInstrVec = std::vector<std::shared_ptr<RVInstr>>;

MODIFY
test/tst_assembler.cpp
test/tst_assembler.cpp
@@ -29,7 +29,9 @@ private:
void tst_Assembler::tst_simpleprogram() {
auto assembler = RV32I_Assembler({});
- QStringList program = QStringList() << ".word 1, 2, 3"
+ QStringList program = QStringList() << ".data"
+ << "B: .word 1, 2, 2"
+ << ".text"
<< "addi a0 a0 123 # Hello world"
<< "nop";
auto res = assembler.assemble(program);
@@ -37,7 +39,7 @@ void tst_Assembler::tst_simpleprogram() {
res.errors.print();
QFAIL("Errors during assembly");
}
- auto disres = assembler.disassemble(res.program);
+ auto disres = assembler.disassemble(res.program.segments[Segment::text]);
return;
}
@@ -53,7 +55,7 @@ void tst_Assembler::tst_simpleWithBranch() {
if (res.errors.size() != 0) {
res.errors.print();
}
- auto disres = assembler.disassemble(res.program);
+ auto disres = assembler.disassemble(res.program.segments[Segment::text]);
return;
}

[Assembler] Implement segment changing directives