Skip to content

Commit

Permalink
Add MIPS32 Assembler
Browse files Browse the repository at this point in the history
Add mips32i_assembler
Add instructions and pseudoinstructions to mips_i_ext.h
Add instructions format at mipsassembler_common.h
Add mipsrelocations.h to take 16MSB and 16LSB
Edit instruction.h to for Signed, Unsigned  and Branch instructions of MIPS32
  • Loading branch information
SteliosKaragiorgis committed Oct 5, 2023
1 parent 6082d75 commit db3e9ed
Show file tree
Hide file tree
Showing 7 changed files with 538 additions and 67 deletions.
8 changes: 8 additions & 0 deletions src/assembler/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
create_ripes_lib(assembler LINK_TO_RIPES_LIB)

target_sources(assembler_lib
PRIVATE
mips32i_assembler.h mips32i_assembler.cpp
mipsrelocations.h
mips_i_ext.h
mipsassembler_common.h
)
97 changes: 30 additions & 67 deletions src/assembler/instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ struct Field {
const ReverseSymbolMap &symbolMap,
LineTokens &line) const = 0;

/// Return the set of bitranges which constitutes this field.
/// Return the set of bitranges which consitutes this field.
virtual std::vector<BitRange> bitRanges() const = 0;
const unsigned tokenIndex;
};
Expand Down Expand Up @@ -217,17 +217,8 @@ struct Imm : public Field<Reg_T> {
using Reg_T_S = typename std::make_signed<Reg_T>::type;
using Reg_T_U = typename std::make_unsigned<Reg_T>::type;

enum class Repr { Unsigned, Signed, Hex };
static Radix reprToRadix(Repr repr) {
if (repr == Repr::Unsigned)
return Radix::Unsigned;
if (repr == Repr::Signed)
return Radix::Signed;
if (repr == Repr::Hex)
return Radix::Hex;
return Radix::Unsigned;
}
enum class SymbolType { None, Relative, Absolute };
enum class Repr { Unsigned, UnsignedMips, Signed, SignedMips, Hex };
enum class SymbolType { None, Relative, Absolute, MipsBranch };
/**
* @brief Imm
* @param _tokenIndex: Index within a list of decoded instruction tokens that
Expand All @@ -248,20 +239,17 @@ struct Imm : public Field<Reg_T> {
: Field<Reg_T>(_tokenIndex), parts(_parts), width(_width), repr(_repr),
symbolType(_symbolType), symbolTransformer(_symbolTransformer) {}

int64_t getImm(const QString &immToken, bool &success,
ImmConvInfo &convInfo) const {
return repr == Repr::Signed
? getImmediateSext32(immToken, success, &convInfo)
: getImmediate(immToken, success, &convInfo);
int64_t getImm(const QString &immToken, bool &success) const {
return repr == Repr::Signed ? getImmediateSext32(immToken, success)
: getImmediate(immToken, success);
}

std::optional<Error>
apply(const TokenizedSrcLine &line, Instr_T &instruction,
FieldLinkRequest<Reg_T> &linksWithSymbol) const override {
bool success;
const Token &immToken = line.tokens[this->tokenIndex];
ImmConvInfo convInfo;
Reg_T_S value = getImm(immToken, success, convInfo);
Reg_T_S value = getImm(immToken, success);

if (!success) {
// Could not directly resolve immediate. Register it as a symbol to link
Expand All @@ -272,8 +260,7 @@ struct Imm : public Field<Reg_T> {
return {};
}

if (auto res = checkFitsInWidth(value, line, convInfo, immToken);
res.isError())
if (auto res = checkFitsInWidth(value, line); res.isError())
return res.error();

for (const auto &part : parts) {
Expand All @@ -282,64 +269,37 @@ struct Imm : public Field<Reg_T> {
return std::nullopt;
}

Result<> checkFitsInWidth(Reg_T_S value, const Location &sourceLine,
ImmConvInfo &convInfo,
QString token = QString()) const {
bool err = false;
if (repr != Repr::Signed) {
if (!isUInt(width, value)) {
err = true;
if (token.isEmpty())
token = QString::number(static_cast<Reg_T_U>(value));
}
} else {

// In case of a bitwize (binary or hex) radix, interpret the value as
// legal if it fits in the width of this immediate (equal to an unsigned
// immediate check). e.g. a signed immediate value of 12 bits must be able
// to accept 0xAFF.
bool isBitwize =
convInfo.radix == Radix::Hex || convInfo.radix == Radix::Binary;
if (isBitwize) {
err = !isUInt(width, value);
}

if (!isBitwize || (isBitwize && err)) {
// A signed representation using an integer value in assembly OR a
// negative bitwize value which is represented in its full length (e.g.
// 0xFFFFFFF1).
err = !isInt(width, value);
}

if (err)
if (token.isEmpty())
token = QString::number(static_cast<Reg_T_S>(value));
}

if (err) {
return Error(sourceLine, "Immediate value '" + token +
"' does not fit in " +
Result<> checkFitsInWidth(Reg_T_S value, const Location &sourceLine) const {
if (!((repr == Repr::Signed || repr == Repr::SignedMips)
? isInt(width, value)
: (isUInt(width, value)))) {
const QString v = repr == Repr::Signed || repr == Repr::SignedMips
? QString::number(static_cast<Reg_T_S>(value))
: QString::number(static_cast<Reg_T_U>(value));
return Error(sourceLine, "Immediate value '" + v + "' does not fit in " +
QString::number(width) + " bits");
}

return Result<>::def();
}

Result<> applySymbolResolution(const Location &loc, Reg_T symbolValue,
Instr_T &instruction, Reg_T address) const {
ImmConvInfo convInfo;
convInfo.radix = reprToRadix(repr);
Reg_T adjustedValue = symbolValue;
if (symbolType == SymbolType::Relative)
adjustedValue -= address;

if (symbolTransformer)
adjustedValue = symbolTransformer(adjustedValue);

if (auto res = checkFitsInWidth(adjustedValue, loc, convInfo);
res.isError())
if (auto res = checkFitsInWidth(adjustedValue, loc); res.isError())
return res.error();

if (repr == Repr::SignedMips)
adjustedValue = (adjustedValue - 1) / 4;

if (repr == Repr::UnsignedMips)
adjustedValue = adjustedValue / 4;

for (const auto &part : parts)
part.apply(adjustedValue, instruction);

Expand All @@ -353,17 +313,20 @@ struct Imm : public Field<Reg_T> {
for (const auto &part : parts) {
part.decode(reconstructed, instruction);
}
if (repr == Repr::Signed) {
if (repr == Repr::Signed || repr == Repr::SignedMips) {
line.push_back(QString::number(vsrtl::signextend(reconstructed, width)));
} else if (repr == Repr::Unsigned) {
} else if (repr == Repr::Unsigned || repr == Repr::UnsignedMips) {
line.push_back(QString::number(reconstructed));
} else {
line.push_back("0x" + QString::number(reconstructed, 16));
}

if (symbolType != SymbolType::None) {
const int value = vsrtl::signextend(reconstructed, width);
const Reg_T symbolAddress =
int value =
(vsrtl::signextend(reconstructed, width) +
(repr == Repr::SignedMips ? 1 : 0)) *
((repr == Repr::SignedMips || repr == Repr::UnsignedMips) ? 4 : 1);
Reg_T symbolAddress =
value + (symbolType == SymbolType::Absolute ? 0 : address);
if (symbolMap.count(symbolAddress)) {
line.push_back("<" + symbolMap.at(symbolAddress).v + ">");
Expand Down
51 changes: 51 additions & 0 deletions src/assembler/mips32i_assembler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "mips32i_assembler.h"
#include "gnudirectives.h"
#include "mipsrelocations.h"
#include "ripessettings.h"

#include <QByteArray>
#include <algorithm>

#include "mips_i_ext.h"

namespace Ripes {
namespace Assembler {

MIPS32I_Assembler::MIPS32I_Assembler(const ISAInfo<ISA::MIPS32I> *isa)
: Assembler<Reg_T>(isa) {
auto [instrs, pseudos] = initInstructions(isa);

auto directives = gnuDirectives();
auto relocations = mipsRelocations<Reg_T>();
initialize(instrs, pseudos, directives, relocations);

// Initialize segment pointers and monitor settings changes to segment
// pointers
connect(RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_TEXTSTART),
&SettingObserver::modified, this, [this](const QVariant &value) {
setSegmentBase(".text", value.toULongLong());
});
RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_TEXTSTART)->trigger();
connect(RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_DATASTART),
&SettingObserver::modified, this, [this](const QVariant &value) {
setSegmentBase(".data", 268500992);
});
RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_DATASTART)->trigger();
connect(RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_BSSSTART),
&SettingObserver::modified, this, [this](const QVariant &value) {
setSegmentBase(".bss", value.toULongLong());
});
RipesSettings::getObserver(RIPES_SETTING_ASSEMBLER_BSSSTART)->trigger();
}

std::tuple<MIPS32I_Assembler::_InstrVec, MIPS32I_Assembler::_PseudoInstrVec>
MIPS32I_Assembler::initInstructions(const ISAInfo<ISA::MIPS32I> *isa) const {
_InstrVec instructions;
_PseudoInstrVec pseudoInstructions;

MIPS_I<Reg_T>::enable(isa, instructions, pseudoInstructions);
return {instructions, pseudoInstructions};
}

} // namespace Assembler
} // namespace Ripes
28 changes: 28 additions & 0 deletions src/assembler/mips32i_assembler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#pragma once

#include <QObject>
#include <functional>

#include "assembler.h"
#include "mipsassembler_common.h"

namespace Ripes {
namespace Assembler {

class MIPS32I_Assembler : public QObject, public Assembler<uint32_t> {
Q_OBJECT

public:
using Reg_T = uint32_t;
MIPS32I_Assembler(const ISAInfo<ISA::MIPS32I> *isa);

private:
std::tuple<_InstrVec, _PseudoInstrVec>
initInstructions(const ISAInfo<ISA::MIPS32I> *isa) const;

protected:
QChar commentDelimiter() const override { return '#'; }
};

} // namespace Assembler
} // namespace Ripes
Loading

0 comments on commit db3e9ed

Please sign in to comment.