// // Created by black on 07.07.25. // #include "Alu.h" #include #include #include "Memory.h" #include "Register.h" #include "../Manager.h" #include "../ProgramLoader.h" void Alu::calculate(const std::vector &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(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(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; }