Files
riscv-emulator/src/components/Alu.cpp
2025-07-08 20:31:11 +02:00

204 lines
9.8 KiB
C++

//
// Created by black on 07.07.25.
//
#include "Alu.h"
#include <cstdint>
#include <vector>
#include "Memory.h"
#include "Register.h"
#include "../Manager.h"
#include "../ProgramLoader.h"
void Alu::calculate(const std::vector<std::string> &commandVector) {
/// Falls die aktuelle Zeile ein Label ist, return
if (const auto length = commandVector.at(0).size(); commandVector.at(0).at(length - 1) == ':') {
return;
}
/// Extrahiere den eigentlichen RISC-V Befehl für die ALU
const std::string &command = commandVector.at(0);
/// Extrahiere die Argumente des Befehls
const auto arg1 = parseArgument(commandVector.at(1));
/// Wandle Vektor[2] (das zweite Argument) nur um, falls es kein SW/LW Argument ist
int arg2 = 0;
if (commandVector.at(2).find('(') != std::string::npos) {
arg2 = parseAddress(commandVector.at(2));
} else {
arg2 = parseArgument(commandVector.at(2));
}
/// Wandle Vektor[3] (das dritte Argument) nur um, falls dieser existiert und kein immediate Wert ist
int arg3 = 0;
if (commandVector.size() > 3) {
if (const std::string &arg = commandVector.at(3); isRegister(arg) || isImmediate(arg)) {
arg3 = parseArgument(arg);
}
}
if (command == "add") {
/// Addition Befehl
/// Hole die Werte beider angegebener Register
const auto register1 = Register::getInstance().getRegister(arg2);
const auto register2 = Register::getInstance().getRegister(arg3);
/// Addiere die beiden Werte und schreibe das Ergebnis ins angegebene Register zurück
const auto result = register1 + register2;
Register::getInstance().setRegister(arg1, result);
} else if (command == "sub") {
/// Subtraktion Befehl
/// Hole die Werte beider angegebener Register
const auto register1 = Register::getInstance().getRegister(arg2);
const auto register2 = Register::getInstance().getRegister(arg3);
/// Subtrahiere die beiden Werte und schreibe das Ergebnis ins angegebene Register zurück
const auto result = register1 - register2;
Register::getInstance().setRegister(arg1, result);
} else if (command == "and") {
/// Binärer und Befehl
/// Hole die Werte beider angegebener Register
const auto register1 = Register::getInstance().getRegister(arg2);
const auto register2 = Register::getInstance().getRegister(arg3);
/// Verknüpfe die beiden Werte binär und schreibe das Ergebnis ins angegebene Register zurück
const auto result = register1 & register2;
Register::getInstance().setRegister(arg1, result);
} else if (command == "or") {
/// Binärer oder Befehl
/// Hole die Werte beider angegebener Register
const auto register1 = Register::getInstance().getRegister(arg2);
const auto register2 = Register::getInstance().getRegister(arg3);
/// Unterscheide die beiden Werte binär und schreibe das Ergebnis ins angegebene Register zurück
const auto result = register1 | register2;
Register::getInstance().setRegister(arg1, result);
} else if (command == "xor") {
/// Binärer exklusiver oder Befehl
/// Hole die Werte beider angegebener Register
const auto register1 = Register::getInstance().getRegister(arg2);
const auto register2 = Register::getInstance().getRegister(arg3);
/// Unterscheide die beiden Werte binär exklusiv und schreibe das Ergebnis ins angegebene Register zurück
const auto result = register1 ^ register2;
Register::getInstance().setRegister(arg1, result);
} else if (command == "addi") {
/// Immediate und Befehl
/// Hole Wert1 aus dem angegebenen Register und Wert2 aus dem Befehlsargument
const auto register1 = Register::getInstance().getRegister(arg2);
const auto immediate = arg3;
/// Addiere die beiden Werte und schreibe das Ergebnis ins angegebene Register zurück
const auto result = register1 + immediate;
Register::getInstance().setRegister(arg1, result);
} else if (command == "andi") {
/// Binärer immediate und Befehl
/// Hole Wert1 aus dem angegebnen Register und Wert2 aus dem Befehlsargument
const auto register1 = Register::getInstance().getRegister(arg2);
const auto immediate = arg3;
/// Addiere die beiden Werte und schreibe das Ergebnis ins angegebene Register zurück
const auto result = register1 & immediate;
Register::getInstance().setRegister(arg1, result);
} else if (command == "ori") {
/// Binärer immediate oder Befehl
/// Hole Wert1 aus dem angegebenen Register und Wert2 aus dem Befehlsargument
const auto register1 = Register::getInstance().getRegister(arg2);
const auto immediate = arg3;
/// Unterscheide beide Werte binär und schreibe das Ergebnis ins angegebene Register zurück
const auto result = register1 | immediate;
Register::getInstance().setRegister(arg1, result);
} else if (command == "lw") {
/// Loadword Befehl
/// Hole die Adresse aus dem Befehlsargument und die dazugehörigen Daten aus dem RAM
const auto address = arg2;
const auto data = Memory::getInstance()->load(address);
/// Speichere die Daten im angegeben Register
Register::getInstance().setRegister(arg1, data);
} else if (command == "sw") {
/// Storeword Befehl
/// Hole die Adresse aus dem Befehlsargument und die dazugehörigen Daten aus dem Register
const auto address = arg2;
const auto data = Register::getInstance().getRegister(arg1);
/// Speichere die Daten im angegeben RAM slot
Memory::getInstance()->store(address, data);
} else if (command == "beq") {
const auto register1 = Register::getInstance().getRegister(arg1);
const auto register2 = Register::getInstance().getRegister(arg2);
const auto &label = commandVector.at(3);
const auto streamPos = ProgramLoader::getInstance()->getStreamPosition(label);
if (register1 == register2) {
Manager::getInstance()->setStreamPosition(streamPos);
}
} else if (command == "bne") {
const auto register1 = Register::getInstance().getRegister(arg1);
const auto register2 = Register::getInstance().getRegister(arg2);
const auto &label = commandVector.at(3);
const auto streamPos = ProgramLoader::getInstance()->getStreamPosition(label);
if (register1 != register2) {
Manager::getInstance()->setStreamPosition(streamPos);
}
} else if (command == "jal") {
/// Jump and link Befehl
/// !!! Aufgrund des casts des stream offsets zu int funktioniert das nicht bei großen Quellcode Dateien
/// und führt zu undefiniertem Verhalten !!!
const auto pos = static_cast<int>(Manager::getInstance()->getNextStreamLineOffset().operator std::streamoff());
const auto &label = commandVector.at(2);
const auto streamPos = ProgramLoader::getInstance()->getStreamPosition(label);
Register::getInstance().setRegister(arg1, pos);
Manager::getInstance()->setStreamPosition(streamPos);
} else if (command == "jalr") {
/// Jump and link register Befehl
/// !!! Aufgrund des casts des stream offsets zu int funktioniert das nicht bei großen Quellcode Dateien
/// und führt zu undefiniertem Verhalten !!!
const auto pos = static_cast<int>(Manager::getInstance()->getNextStreamLineOffset().operator std::streamoff());
const auto &label = commandVector.at(3);
const auto streamPosLabel = ProgramLoader::getInstance()->getStreamPosition(label);
const auto posOffset = Register::getInstance().getRegister(arg2);
Register::getInstance().setRegister(arg1, pos);
Manager::getInstance()->setStreamPosition(streamPosLabel);
for (int i = 0; i < posOffset; ++i) {
Manager::getInstance()->gotoNextStreamLine();
}
}
}
int Alu::parseArgument(std::string argument) {
/// Falls das erste Argument ein Register ist, entferne das führende "x"
if (argument.at(0) == 'x') argument = argument.substr(1);
/// Da das Argument noch als String vorliegt, muss es in ein int umgewandelt werden
return std::stoi(argument);
}
int Alu::parseAddress(const std::string &argument) {
/// Finde die Position von '(', trenne die den String dort und
/// addiere die umgewandelten Integer zur Adresse
/// Bsp: 0(x1)
if (const size_t pos = argument.find('('); pos != std::string::npos) {
const auto immediate = std::stoi(argument.substr(0, pos + 1));
const auto register1 = std::stoi(argument.substr(pos + 2));
const auto address = immediate + register1;
return address;
}
return 0;
}
bool Alu::isRegister(const std::string &argument) {
/// Überprüfe, ob das nichtleere Argument mit 'x' startet
if (argument.empty() || argument.at(0) != 'x') return false;
/// Entfernt das voranstehende 'x' und checkt, ob darauf etwas folgt
const std::string numberPart = argument.substr(1);
if (numberPart.empty()) return false;
/// Überprüfe, ob alle Zeichen im restlichen Argument Nummern sind
for (const char c: numberPart) {
if (!std::isdigit(c)) return false;
}
/// Konvertiere zu int und überprüfe, ob es ein Register zwischen inklusive x0 und x32 ist
const int regNum = std::stoi(numberPart);
return regNum >= 0 && regNum <= 32;
}
bool Alu::isImmediate(const std::string &argument) {
if (argument.empty()) return false;
constexpr size_t start = 0;
/// Überprüfe, ob jedes Zeichen im Argument eine Nummer ista
for (size_t i = start; i < argument.size(); ++i) {
if (!std::isdigit(argument.at(i))) return false;
}
return true;
}