[Assembler] Restructure passes
MODIFY
src/assembler/assembler.h
src/assembler/assembler.h
#include "assembler_defines.h"
#include "isainfo.h"
+#include "lexerutilities.h"
#include "matcher.h"
#include <set>
@@ -40,26 +41,34 @@ namespace AssemblerTmp {
template <typename ISA>
class AssemblerBase {
using Instr = Instruction<ISA>;
+ using InstrMap = std::map<QString, std::shared_ptr<Instr>>;
using InstrVec = std::vector<std::shared_ptr<Instr>>;
using PseudoInstr = PseudoInstruction<ISA>;
+ using PseudoInstrMap = std::map<QString, std::shared_ptr<PseudoInstr>>;
using PseudoInstrVec = std::vector<std::shared_ptr<PseudoInstr>>;
public:
Result assemble(const QString& program) const {
const auto programLines = program.split(QRegExp("[\r\n]"));
+ return assemble(programLines);
+ }
+ Result assemble(const QStringList& programLines) const {
Result result;
+ // Tokenize each source line and separate symbol from remainder of tokens
runPass(tokenizedLines, Program, pass0, programLines);
- // Pseudo SourceLine expansion
- runPass(unused, Program, pass1, tokenizedLines);
+ // Pseudo instruction expansion
+ runPass(expandedLines, Program, pass1, tokenizedLines);
- // Symbol mapping
- Symbols symbols;
- runPass(unused2, NoPassResult, pass2, tokenizedLines, symbols);
+ // Assemble & generate symbol map
+ SymbolMap symbolMap;
+ runPass(program, QByteArray, pass2, expandedLines, symbolMap);
- // Machine code translation
- // runPass(unused3, QByteArray, pass3, tokenizedLines, symbols);
+ // Symbol linkage
+ runPass(unused, NoPassResult, pass3, program, symbolMap);
+ result.program = program;
+ return result;
}
QString disassemble(const QByteArray& program) const;
@@ -74,12 +83,36 @@ private:
Errors errors;
Program tokenizedLines;
tokenizedLines.reserve(program.size());
+
+ /** @brief carry
+ * A symbol should refer to the next following assembler line; whether an instruction or directive.
+ * The carry is used to carry over symbol definitions from empty lines onto the next valid line.
+ */
+ Symbols carry;
for (unsigned i = 0; i < program.size(); i++) {
const auto& line = program.at(i);
if (line.isEmpty())
continue;
- runOperation(tokenizedLine, LineTokens, tokenize, program[i]);
- tokenizedLines.push_back({tokenizedLine, i, {}});
+ TokenizedSrcLine tsl;
+ runOperation(tokens, LineTokens, tokenize, program[i]);
+
+ runOperation(directivesAndRest, DirectivesLinePair, splitDirectivesFromLine, tokens, i);
+ tsl.directives = directivesAndRest.first;
+
+ runOperation(symbolsAndRest, SymbolLinePair, splitSymbolsFromLine, directivesAndRest.second, i);
+ tsl.sourceLine = i;
+ tsl.symbols = symbolsAndRest.first;
+ tsl.tokens = symbolsAndRest.second;
+
+ if (!tsl.symbols.empty() && tsl.tokens.empty()) {
+ carry.insert(tsl.symbols.begin(), tsl.symbols.end());
+ } else {
+ if (!tsl.tokens.empty()) {
+ tsl.symbols.insert(carry.begin(), carry.end());
+ carry.clear();
+ }
+ tokenizedLines.push_back(tsl);
+ }
}
if (errors.size() != 0) {
return {errors};
@@ -101,11 +134,23 @@ private:
const auto& tokenizedLine = tokenizedLines.at(i);
runOperation(expandedOps, std::optional<std::vector<LineTokens>>, expandPseudoOp, tokenizedLine);
if (expandedOps) {
- for (const auto& expandedOp : expandedOps.value()) {
- /** @note: Original source line is kept for all resulting lines after pseudo-op expantion */
- expandedLines.push_back({expandedOp, tokenizedLine.source_line, {}});
+ /** @note: Original source line is kept for all resulting lines after pseudo-op expantion.
+ * Labels, directives and comments are only kept for the first expanded op.
+ */
+ const auto& eops = expandedOps.value();
+ for (int j = 0; j < eops.size(); j++) {
+ TokenizedSrcLine tsl;
+ tsl.tokens = eops.at(j);
+ tsl.sourceLine = tokenizedLine.sourceLine;
+ if (j == 0) {
+ tsl.comments = tokenizedLine.comments;
+ tsl.directives = tokenizedLine.directives;
+ tsl.symbols = tokenizedLine.symbols;
+ }
+ expandedLines.push_back(tsl);
}
} else {
+ // This was not a pseudoinstruction; just add line to the set of expanded lines
expandedLines.push_back(tokenizedLine);
}
}
@@ -116,40 +161,49 @@ private:
return {expandedLines};
}
}
+
/**
* @brief pass2
- * Symbol recording. If @return errors is empty, pass succeeded.
+ * Record output address and symbol positions in expanded program. If @return errors is empty, pass succeeded.
*/
- std::variant<Errors, NoPassResult> pass2(Program& tokenizedLines, Symbols& symbols) const {
+ std::variant<Errors, SymbolMap> pass2(Program& tokenizedLines) const {
Errors errors;
- for (auto& line : tokenizedLines) {
- runOperation(splitLine, SymbolLinePair, splitSymbolFromLine, line);
- const auto& [symbol, rest] = splitLine;
- if (!symbol.isEmpty()) {
- if (symbols.count(symbol)) {
- errors.push_back({line.source_line, "Symbol '" + symbol + "' is already defined."});
- }
- line.symbol = symbol;
+ SymbolMap sm;
+
+ // Unless otherwise specified, start at instruction segment
+ uint32_t address;
+ try {
+ address = m_segmentPointers.at(dataSegment());
+ } catch (std::out_of_range const&) {
+ errors.push_back(Error(-1, "No base address specified for segment: " + instrSegment()));
+ return {errors};
}
+
+ for (auto& line : tokenizedLines) {
}
if (errors.size() != 0) {
return {errors};
} else {
- return {NoPassResult()};
+ return {sm};
}
}
/**
- * @brief pass3
+ * @brief pass2
* Machine code translation. If @return errors is empty, pass succeeded.
*/
- std::variant<Errors, QByteArray> pass3(const Program& tokenizedLines, const SymbolMap& symbolMap) const {
+ std::variant<Errors, QByteArray> pass2(const Program& tokenizedLines, SymbolMap& symbolMap) const {
QByteArray program;
Errors errors;
for (const auto& line : tokenizedLines) {
- runOperation(machineCode, QByteArray, assembleInstruction, line, symbolMap);
- program.append(machineCode);
+ runOperation(directiveBytes, std::optional<QByteArray>, assembleDirective, line);
+ if (!directiveBytes) {
+ runOperation(machineCode, uint32_t, assembleInstruction, line);
+ program.append(QByteArray::number(machineCode));
+ } else {
+ program.append(directiveBytes.value());
+ }
}
if (errors.size() != 0) {
return {errors};
@@ -158,51 +212,144 @@ private:
}
}
+ std::variant<Errors, NoPassResult> pass3(QByteArray& program, const SymbolMap& symbolMap) const {
+ return NoPassResult();
+ }
+
protected:
AssemblerBase<ISA>(std::pair<InstrVec, PseudoInstrVec> instructions)
- : m_instructions(instructions.first), m_pseudoInstructions(instructions.second), m_matcher(m_instructions) {}
+ : m_instructions(instructions.first), m_pseudoInstructions(instructions.second), m_matcher(m_instructions) {
+ for (const auto& iter : m_instructions) {
+ const auto instr_name = iter.get()->name();
+ if (m_instructionMap.count(instr_name) != 0) {
+ throw std::runtime_error("Error: instruction with opcode " + instr_name.toStdString() +
+ " has already been registerred.");
+ }
+ m_instructionMap[instr_name] = iter;
+ }
+ for (const auto& iter : m_pseudoInstructions) {
+ const auto instr_name = iter.get()->name();
+ if (m_pseudoInstructionMap.count(instr_name) != 0) {
+ throw std::runtime_error("Error: pseudo-instruction with opcode " + instr_name.toStdString() +
+ " has already been registerred.");
+ }
+ m_pseudoInstructionMap[instr_name] = iter;
+ }
+ }
+
+ 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, std::optional<std::vector<LineTokens>>>
- expandPseudoOp(const SourceLine& line) const = 0;
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));
+ }
- return line.split(splitter);
+ virtual AssembleRes assembleInstruction(const TokenizedSrcLine& line) 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 + "'")};
}
- virtual std::variant<Error, QByteArray> assembleInstruction(const SourceLine& instruction,
- const SymbolMap& symbols) const = 0;
+ return m_instructionMap.at(opcode)->assemble(line);
+ };
+
+ virtual AssembleDirectiveRes assembleDirective(const TokenizedSrcLine& line) const {
+ // @todo: add assembler directive assembling
+ return std::optional<QByteArray>();
+ };
/**
- * @brief splitSymbol
+ * @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> splitSymbolFromLine(const SourceLine& line) const {
- if (line.tokens.size() == 0) {
- return std::pair<QString, LineTokens>(QString(), line.tokens);
+ virtual std::variant<Error, SymbolLinePair> splitSymbolsFromLine(const LineTokens& tokens, int sourceLine) const {
+ if (tokens.size() == 0) {
+ return std::pair<Symbols, LineTokens>({}, tokens);
}
- const bool hasSymbol =
- std::any_of(line.tokens.begin(), line.tokens.end(), [](const auto& s) { return s.contains(':'); });
+ LineTokens remainingTokens;
+ remainingTokens.reserve(tokens.size());
+ Symbols symbols;
+ bool symbolStillAllowed = true;
+ for (const auto& token : tokens) {
+ if (token.contains(':')) {
+ if (symbolStillAllowed) {
+ symbols.insert(QString(token).remove(':'));
+ } else {
+ return Error(sourceLine, "Stray ':' in line");
+ }
+ } else {
+ remainingTokens.append(token);
+ symbolStillAllowed = false;
+ }
+ }
+ return std::pair<Symbols, LineTokens>(symbols, remainingTokens);
+ }
- const auto& firstToken = line.tokens.at(0);
+ virtual std::variant<Error, DirectivesLinePair> splitDirectivesFromLine(const LineTokens& tokens,
+ int sourceLine) const {
+ if (tokens.size() == 0) {
+ return std::pair<Directives, LineTokens>({}, tokens);
+ }
- if (hasSymbol && !firstToken.contains(':')) {
- return Error(line.source_line, "Line must start with symbol");
+ 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");
}
- if (firstToken.count(':') > 1) {
- return Error(line.source_line, "Multiple instances of ':' in label");
+ } else {
+ remainingTokens.append(token);
+ directivesStillAllowed = false;
}
-
- // Valid symbol - parse the symbol and remaining tokens
- auto remainingTokens = line.tokens;
- remainingTokens.removeFirst();
- auto symbol = firstToken;
- symbol.remove(':');
- return std::pair<QString, LineTokens>(symbol, remainingTokens);
}
+ return std::pair<Directives, LineTokens>(directives, remainingTokens);
+ }
+
+ virtual QString instrSegment() const { return QStringLiteral(".text"); }
+ virtual QString dataSegment() const { return QStringLiteral(".data"); }
+ /**
+ * @brief m_instructions is the set of instructions which can be matched from an instruction string as well as be
+ * disassembled from a program.
+ */
const 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.
+ */
const PseudoInstrVec& m_pseudoInstructions;
+ PseudoInstrMap m_pseudoInstructionMap;
+
+ /**
+ * @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;
+
const Matcher<ISA> m_matcher;
};

MODIFY
src/assembler/assembler_defines.h
src/assembler/assembler_defines.h
#include <set>
#include <variant>
-namespace Ripes {
+#include "assemblererror.h"
+namespace Ripes {
namespace AssemblerTmp {
+using LineTokens = QStringList;
using Symbol = QString;
using Symbols = std::set<Symbol>;
-using SymbolMap = std::map<Symbol, unsigned>;
+using Directive = QString;
+using Directives = std::set<Directive>;
+using DirectivesLinePair = std::pair<Directives, LineTokens>;
+using SymbolMap = std::map<Symbol, uint32_t>;
using ReverseSymbolMap = std::map<uint32_t, Symbol>;
-using LineTokens = QStringList;
-struct SourceLine {
+using AssembleDirectiveRes = std::variant<Error, std::optional<QByteArray>>;
+struct TokenizedSrcLine {
+ Symbols symbols;
+ LineTokens comments;
LineTokens tokens;
- unsigned source_line = 0;
- QString symbol;
+ Directives directives;
+ unsigned sourceLine = 0;
+ uint32_t programAddress = -1;
};
-using SymbolLinePair = std::pair<Symbol, LineTokens>;
-using Program = std::vector<SourceLine>;
+using SymbolLinePair = std::pair<Symbols, LineTokens>;
+using Program = std::vector<TokenizedSrcLine>;
using NoPassResult = std::monostate;
-// An error is defined as a reference to a source line index + an error string
-using Error = std::pair<unsigned, QString>;
-using Errors = std::vector<Error>;
/**
* @brief The Result struct
* An assembly result is determined to be valid iff errors is empty.

ADD
src/assembler/assemblererror.h
+#pragma once
+
+#include <QString>
+#include <iostream>
+#include <vector>
+
+namespace Ripes {
+namespace AssemblerTmp {
+
+// An error is defined as a reference to a source line index + an error string
+using Error = std::pair<unsigned, QString>;
+struct Errors : public std::vector<Error> {
+ void print() {
+ for (const auto& iter : *this) {
+ std::cout << "line " << iter.first << ": " << iter.second.toStdString() << "\n";
+ }
+ std::cout << std::flush;
+ }
+};
+
+} // namespace AssemblerTmp
+} // namespace Ripes

MODIFY
src/assembler/instruction.h
src/assembler/instruction.h
@@ -15,9 +15,9 @@ namespace Ripes {
namespace AssemblerTmp {
namespace {
-using AssembleRes = std::variant<AssemblerTmp::Error, uint32_t>;
-using PseudoExpandRes = std::variant<AssemblerTmp::Error, std::vector<AssemblerTmp::LineTokens>>;
-using DisassembleRes = std::variant<AssemblerTmp::Error, AssemblerTmp::LineTokens>;
+using AssembleRes = std::variant<Error, uint32_t>;
+using PseudoExpandRes = std::variant<Error, std::optional<std::vector<AssemblerTmp::LineTokens>>>;
+using DisassembleRes = std::variant<Error, AssemblerTmp::LineTokens>;
} // namespace
struct BitRange {
@@ -39,7 +39,7 @@ struct BitRange {
struct Field {
Field() = default;
virtual ~Field() = default;
- virtual std::optional<AssemblerTmp::Error> apply(const AssemblerTmp::SourceLine& line,
+ virtual std::optional<AssemblerTmp::Error> apply(const AssemblerTmp::TokenizedSrcLine& line,
uint32_t& instruction) const = 0;
virtual std::optional<AssemblerTmp::Error> decode(const uint32_t instruction, const uint32_t address,
const ReverseSymbolMap* symbolMap,
@@ -68,7 +68,8 @@ struct Opcode : public Field {
*/
Opcode(const QString& _name, std::vector<OpPart> _opParts) : name(_name), opParts(_opParts) {}
- std::optional<AssemblerTmp::Error> apply(const AssemblerTmp::SourceLine&, uint32_t& instruction) const override {
+ std::optional<AssemblerTmp::Error> apply(const AssemblerTmp::TokenizedSrcLine&,
+ uint32_t& instruction) const override {
for (const auto& opPart : opParts) {
instruction |= opPart.range.apply(opPart.value);
}
@@ -94,13 +95,13 @@ struct Reg : public Field {
*/
Reg(unsigned tokenIndex, BitRange range) : m_tokenIndex(tokenIndex), m_range(range) {}
Reg(unsigned tokenIndex, unsigned _start, unsigned _stop) : m_tokenIndex(tokenIndex), m_range({_start, _stop}) {}
- std::optional<AssemblerTmp::Error> apply(const AssemblerTmp::SourceLine& line,
+ std::optional<AssemblerTmp::Error> apply(const AssemblerTmp::TokenizedSrcLine& line,
uint32_t& instruction) const override {
bool success;
const QString& regToken = line.tokens[m_tokenIndex];
const uint32_t reg = ISA::instance()->regNumber(regToken, success);
if (!success) {
- return AssemblerTmp::Error(line.source_line, "Unknown register '" + regToken + "'");
+ return AssemblerTmp::Error(line.sourceLine, "Unknown register '" + regToken + "'");
}
instruction |= m_range.apply(reg);
}
@@ -142,7 +143,7 @@ struct Imm : public Field {
SymbolType _symbolType = SymbolType::None)
: tokenIndex(_tokenIndex), parts(_parts), width(_width), repr(_repr), symbolType(_symbolType) {}
- std::optional<AssemblerTmp::Error> apply(const AssemblerTmp::SourceLine& line,
+ std::optional<AssemblerTmp::Error> apply(const AssemblerTmp::TokenizedSrcLine& line,
uint32_t& instruction) const override {
// @Todo: decode immediate from token (appropriate bit width!), apply each ImmPart with immediate to instruction
}
@@ -184,17 +185,18 @@ class Instruction {
public:
Instruction(Opcode opcode, const std::vector<std::shared_ptr<Field>>& fields)
: m_opcode(opcode), m_expectedTokens(1 /*opcode*/ + fields.size()), m_fields(fields) {
- m_assembler = [this](const AssemblerTmp::SourceLine& line) {
+ m_assembler = [](const Instruction& _this, const AssemblerTmp::TokenizedSrcLine& line) {
uint32_t instruction = 0;
- m_opcode.apply(line, instruction);
- for (const auto& field : m_fields)
+ _this.m_opcode.apply(line, instruction);
+ for (const auto& field : _this.m_fields)
field->apply(line, instruction);
return instruction;
};
- m_disassembler = [this](const uint32_t instruction, const uint32_t address, const ReverseSymbolMap* symbolMap) {
+ m_disassembler = [](const Instruction& _this, const uint32_t instruction, const uint32_t address,
+ const ReverseSymbolMap* symbolMap) {
AssemblerTmp::LineTokens line;
- m_opcode.decode(instruction, address, symbolMap, line);
- for (const auto& field : m_fields) {
+ _this.m_opcode.decode(instruction, address, symbolMap, line);
+ for (const auto& field : _this.m_fields) {
if (auto error = field->decode(instruction, address, symbolMap, line)) {
return DisassembleRes(*error);
}
@@ -203,27 +205,33 @@ public:
};
}
- AssembleRes assemble(const AssemblerTmp::SourceLine& line) {
+ AssembleRes assemble(const AssemblerTmp::TokenizedSrcLine& line) const {
if (line.tokens.length() != m_expectedTokens) {
- return AssemblerTmp::Error(line.source_line, "Instruction '" + m_opcode.name + "' expects " +
+ return AssemblerTmp::Error(line.sourceLine, "Instruction '" + m_opcode.name + "' expects " +
QString::number(m_expectedTokens - 1) +
" tokens, but got " +
QString::number(line.tokens.length() - 1));
}
- return m_assembler(line);
+ return m_assembler(*this, line);
}
DisassembleRes disassemble(const uint32_t instruction, const uint32_t address,
const ReverseSymbolMap* symbolMap) const {
- return m_disassembler(instruction, address, symbolMap);
+ return m_disassembler(*this, instruction, address, symbolMap);
}
const Opcode& getOpcode() const { return m_opcode; }
const QString& name() const { return m_opcode.name; }
+ /**
+ * @brief size
+ * @return size of assembled instruction, in byte
+ */
+ const unsigned& size() const { return 4; }
private:
- std::function<AssembleRes(const AssemblerTmp::SourceLine&)> m_assembler;
- std::function<DisassembleRes(const uint32_t, const uint32_t, const ReverseSymbolMap*)> m_disassembler;
+ std::function<AssembleRes(const Instruction&, const AssemblerTmp::TokenizedSrcLine&)> m_assembler;
+ std::function<DisassembleRes(const Instruction&, const uint32_t, const uint32_t, const ReverseSymbolMap*)>
+ m_disassembler;
const Opcode m_opcode;
const int m_expectedTokens;
@@ -235,22 +243,21 @@ class PseudoInstruction {
public:
PseudoInstruction(
const QString& opcode, const std::vector<std::shared_ptr<Field>>& fields,
- const std::function<PseudoExpandRes(const PseudoInstruction&, const AssemblerTmp::SourceLine&)>& expander)
+ const std::function<PseudoExpandRes(const PseudoInstruction&, const AssemblerTmp::TokenizedSrcLine&)>& expander)
: m_opcode(opcode), m_expectedTokens(1 /*opcode*/ + fields.size()), m_fields(fields), m_expander(expander) {}
- PseudoExpandRes expand(const AssemblerTmp::SourceLine& line) {
+ PseudoExpandRes expand(const AssemblerTmp::TokenizedSrcLine& line) {
if (line.tokens.length() != m_expectedTokens) {
return AssemblerTmp::Error(
- line.source_line, "Instruction '" + m_opcode + "' expects " + QString::number(m_expectedTokens - 1) +
+ line.sourceLine, "Instruction '" + m_opcode + "' expects " + QString::number(m_expectedTokens - 1) +
" tokens, but got " + QString::number(line.tokens.length() - 1));
}
- return m_expander(line);
+ return m_expander(*this, line);
}
const QString& name() const { return m_opcode; }
-
/**
* @brief reg() and imm() return dummy register and immediate expected tokens for a pseudo instruction.
* Does not relate to actual regs/imms in an instruction word, but solely used for a pseudoinstruction to
@@ -260,7 +267,7 @@ public:
static std::shared_ptr<Imm> imm() { return std::make_shared<Imm>(0, 0, Imm::Repr::Hex, std::vector<ImmPart>{}); }
private:
- std::function<PseudoExpandRes(const PseudoInstruction& /*this*/, const AssemblerTmp::SourceLine&)> m_expander;
+ std::function<PseudoExpandRes(const PseudoInstruction& /*this*/, const AssemblerTmp::TokenizedSrcLine&)> m_expander;
const QString m_opcode;
const int m_expectedTokens;

MODIFY
test/tst_assembler.cpp
test/tst_assembler.cpp
@@ -14,14 +14,57 @@ class tst_Assembler : public QObject {
Q_OBJECT
private slots:
+ void tst_simpleprogram();
+ void tst_segment();
void tst_matcher();
+ void tst_label();
+ void tst_labelWithPseudo();
private:
std::vector<std::shared_ptr<Instruction<RVISA>>> createInstructions();
};
+void tst_Assembler::tst_simpleprogram() {
+ auto assembler = RV32I_Assembler({});
+ QStringList program = QStringList() << "addi a0 a0 2"
+ << "nop";
+ auto res = assembler.assemble(program);
+ if (res.errors.size() != 0) {
+ res.errors.print();
+ }
+ return;
+}
+
+void tst_Assembler::tst_label() {
+ auto assembler = RV32I_Assembler({});
+ QStringList program = QStringList() << "A:"
+ << ""
+ << "B: C:"
+ << "D: E: addi a0 a0 -1";
+ auto res = assembler.assemble(program);
+ return;
+}
+
+void tst_Assembler::tst_segment() {
+ auto assembler = RV32I_Assembler({});
+ QStringList program = QStringList() << ".data nop"
+ << "nop"
+ << ".text .word"
+ << "nop"
+ << ".data"
+ << "nop";
+ auto res = assembler.assemble(program);
+ if (res.errors.size() != 0) {
+ res.errors.print();
+ }
+ return;
+}
+
+void tst_Assembler::tst_labelWithPseudo() {}
+
void tst_Assembler::tst_matcher() {
auto assembler = RV32I_Assembler({});
+ assembler.getMatcher().print();
std::vector<std::pair<QString, unsigned>> toMatch = {
{"beq", 0b11111110000000000000111011100011}, {"addi", 0b00000111101100010000000010010011},

[Assembler] Restructure passes