diff --git a/src/Manager.cpp b/src/Manager.cpp index aa69f85..d53b7ad 100644 --- a/src/Manager.cpp +++ b/src/Manager.cpp @@ -5,32 +5,35 @@ #include "Manager.h" #include -#include #include #include "components/Alu.h" namespace fs = std::filesystem; Manager::~Manager() { + std::cout << "Schließe Quellcode Datei: '" << m_path << "'" << "\n" << std::flush; m_programFile.close(); } -Manager *Manager::getInstance() { +Manager &Manager::getInstance() { static Manager instance; - return &instance; + return instance; } int Manager::run() { if (!m_programFile.is_open()) { std::cerr << "Bitte zuerst die init() Methode aufrufen!" << "\n" << std::flush; } - ProgramLoader::getInstance()->indexFile(m_programFile); - /// Die Position des Streams (virtueller Lesekopf) muss nach dem Indexen wieder zurückgesetzt werden + ProgramLoader::getInstance().indexFile(m_programFile); + // Die Position des Streams (virtueller Lesekopf) muss nach dem Indexen wieder zurückgesetzt werden m_programFile.clear(); m_programFile.seekg(0); + + // Bearbeite jede Zeile des Quellcodes in der ALU std::string line; while (std::getline(m_programFile, line)) { auto lineVector = ProgramLoader::parseLine(line); + // Sollte die aktuelle Zeile leer sein, überspringe die ALU if (lineVector.empty()) continue; Alu::calculate(lineVector); } @@ -48,11 +51,15 @@ std::streampos Manager::getStreamPosition() { void Manager::init(const std::string &program_path) { m_path = program_path; std::cout << "Öffne Quellcode Datei: '" << m_path << "'" << "\n" << std::flush; + + //Überprüfe, ob die Datei existiert if (!fs::exists(m_path)) { std::cerr << "Datei existiert nicht: " << m_path << "\n" << std::flush; return; } m_programFile.open(m_path); + + // Sollte die Datei nicht geöffnet worden sein, gib den Fehler aus if (!m_programFile.is_open()) { std::cerr << "Datei konnte nicht geöffnet werden: " << m_path << "\n" << std::flush; std::cerr << "fail (z.B. Zugriffsrechte): " << m_programFile.fail() << "\n" << std::flush; @@ -62,12 +69,14 @@ void Manager::init(const std::string &program_path) { } std::streampos Manager::getNextStreamLineOffset() { - /// Speichert das aktuelle stream offset + // Speichert das aktuelle stream offset const auto positionBefore = m_programFile.tellg(); - /// Geht eine Zeile nach vorne zum nächsten Befehl (PC+4) und speichert den offset + + // Geht eine Zeile nach vorne zum nächsten Befehl (PC+4) und speichert den offset gotoNextStreamLine(); const auto positionAfter = m_programFile.tellg(); - /// Geht zum offset vom Anfang zurück und gibt die nächste Adresse (PC+4) zurück + + // Geht zum offset vom Anfang zurück und gibt die nächste Adresse (PC+4) zurück m_programFile.seekg(positionBefore); return positionAfter; } diff --git a/src/Manager.h b/src/Manager.h index 140706b..3ffee1e 100644 --- a/src/Manager.h +++ b/src/Manager.h @@ -9,11 +9,11 @@ #include "ProgramLoader.h" -/// Die Manager Instanz sorgt für die Koordination von user input, Datei einlesen, code execution und anderen Aktionen. +// Die Manager Instanz sorgt für die Koordination von user input, Datei einlesen, code execution und anderen Aktionen. class Manager { private: - /// Die Instanz des Managers soll mit einem immutable path zum Programm erstellt werden + // Die Instanz des Managers soll mit einem immutable path zum Programm erstellt werden Manager() = default; std::string m_path; @@ -21,7 +21,7 @@ private: std::ifstream m_programFile; public: - /// Singleton Logik + // Singleton Logik Manager(const Manager &) = delete; Manager(const Manager &&) = delete; @@ -37,7 +37,7 @@ public: * * @return Die Singleton Instance des Managers */ - static Manager *getInstance(); + static Manager &getInstance(); /** * Die Hauptmethode des Emulators. diff --git a/src/ProgramLoader.cpp b/src/ProgramLoader.cpp index f01e1e9..213d35d 100644 --- a/src/ProgramLoader.cpp +++ b/src/ProgramLoader.cpp @@ -5,44 +5,58 @@ #include "ProgramLoader.h" #include -ProgramLoader *ProgramLoader::getInstance() { +ProgramLoader &ProgramLoader::getInstance() { static ProgramLoader instance; - return &instance; + return instance; } [[nodiscard]] std::vector ProgramLoader::parseLine(const std::string &input) { + // Sollte die aktuelle Zeile leer sein, gib einen leeren Vektor zurück if (input.empty()) return std::vector{}; std::vector output{}; - /// Konvertiere den Input String in einen IStringStream, damit dieser bei Leerzeichen gesplittet werden kann + // Konvertiere den Input String in einen IStringStream, damit dieser bei Leerzeichen gesplittet werden kann std::istringstream iss(input); std::string out; do { out.clear(); - /// Trenne den IStringStream bei jedem Leerzeichen und füge die Befehl(e)/-sargumente dem Output hinzu + + // Trenne den IStringStream bei jedem Leerzeichen und füge die Befehl(e)/-sargumente dem Output hinzu std::getline(iss, out, ' '); if (out.empty()) break; - /// Stoppe, sobald ein Kommentar im Source Code vorkommt + + // Entferne alle Leerzeichen hinter dem Befehl/Argument + while (out.at(out.length() - 1) == ' ') { + out.erase(out.length() - 1); + } + + // Stoppe, sobald ein Kommentar im Source Code vorkommt if (out.at(0) != '#') { if (out.at(out.length() - 1) == ',') { out.erase(out.length() - 1); } + output.push_back(out); } else { break; } } while (!out.empty()); + return output; } void ProgramLoader::indexFile(std::ifstream &m_programFile) { + // Setze den Lesekop zurück m_programFile.clear(); m_programFile.seekg(0); + std::string line; - /// Parse Zeile für Zeile + // Parse Zeile für Zeile while (std::getline(m_programFile, line)) { + // Parst die aktuelle Zeile und springt zur nächsten, falls die aktuelle leer ist auto lineVector = parseLine(line); if (lineVector.empty()) continue; - /// Sobald ein Label gefunden wurde, speichere die Position des Streams + + // Sobald ein Label gefunden wurde, speichere die Position des Streams if (const auto first = lineVector.begin(); first->at(first->length() - 1) == ':') { m_labels[first->substr(0, first->length() - 1)] = m_programFile.tellg(); } diff --git a/src/ProgramLoader.h b/src/ProgramLoader.h index 7ad0d90..7ae44c9 100644 --- a/src/ProgramLoader.h +++ b/src/ProgramLoader.h @@ -9,7 +9,7 @@ #include #include -/// Der ProgramLoader, zuständig für das Lesen und Parsen des Quellcodes +// Der ProgramLoader, zuständig für das Lesen und Parsen des Quellcodes class ProgramLoader { private: @@ -21,7 +21,7 @@ private: std::map m_labels; public: - /// Singleton Logik + // Singleton Logik ProgramLoader(const ProgramLoader &) = delete; ProgramLoader(const ProgramLoader &&) = delete; @@ -35,7 +35,7 @@ public: * * @return ProgramLoader Instanz */ - static ProgramLoader *getInstance(); + static ProgramLoader &getInstance(); /** * Parst eine Zeile des Assembly Codes und gibt diese als sortierten Vektor zurück diff --git a/src/components/Alu.cpp b/src/components/Alu.cpp index 8b664d8..7236af3 100644 --- a/src/components/Alu.cpp +++ b/src/components/Alu.cpp @@ -5,6 +5,7 @@ #include "Alu.h" #include +#include #include #include "Memory.h" @@ -13,6 +14,11 @@ #include "../ProgramLoader.h" void Alu::calculate(const std::vector &commandVector) { + auto ® = Register::getInstance(); + auto &mem = Memory::getInstance(); + const auto &pl = ProgramLoader::getInstance(); + auto &man = Manager::getInstance(); + // Falls die aktuelle Zeile ein Label ist, return if (const auto length = commandVector.at(0).size(); commandVector.at(0).at(length - 1) == ':') { return; @@ -23,13 +29,16 @@ void Alu::calculate(const std::vector &commandVector) { // 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 + + // Wandle Vektor[2] (das zweite Argument) in eine Adresse um, falls es ein SW/LW Argument ist int arg2 = 0; if (commandVector.at(2).find('(') != std::string::npos) { arg2 = parseAddress(commandVector.at(2)); } else { + // Ansonsten wandle es in ein Registerargument um 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) { @@ -41,124 +50,126 @@ void Alu::calculate(const std::vector &commandVector) { 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); + const auto register1 = reg.getRegister(arg2); + const auto register2 = reg.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); + reg.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); + const auto register1 = reg.getRegister(arg2); + const auto register2 = reg.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); + reg.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); + const auto register1 = reg.getRegister(arg2); + const auto register2 = reg.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); + reg.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); + const auto register1 = reg.getRegister(arg2); + const auto register2 = reg.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); + reg.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); + const auto register1 = reg.getRegister(arg2); + const auto register2 = reg.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); + reg.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 register1 = reg.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); + reg.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 register1 = reg.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); + reg.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 register1 = reg.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); + reg.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); + const auto data = mem.load(address); // Speichere die Daten im angegeben Register - Register::getInstance().setRegister(arg1, data); + reg.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); + const auto data = reg.getRegister(arg1); // Speichere die Daten im angegeben RAM slot - Memory::getInstance()->store(address, data); + mem.store(address, data); } else if (command == "beq") { - const auto register1 = Register::getInstance().getRegister(arg1); - const auto register2 = Register::getInstance().getRegister(arg2); + const auto register1 = reg.getRegister(arg1); + const auto register2 = reg.getRegister(arg2); const auto &label = commandVector.at(3); - const auto streamPos = ProgramLoader::getInstance()->getStreamPosition(label); + const auto streamPos = pl.getStreamPosition(label); if (register1 == register2) { - Manager::getInstance()->setStreamPosition(streamPos); + man.setStreamPosition(streamPos); } } else if (command == "bne") { - const auto register1 = Register::getInstance().getRegister(arg1); - const auto register2 = Register::getInstance().getRegister(arg2); + const auto register1 = reg.getRegister(arg1); + const auto register2 = reg.getRegister(arg2); const auto &label = commandVector.at(3); - const auto streamPos = ProgramLoader::getInstance()->getStreamPosition(label); + const auto streamPos = pl.getStreamPosition(label); if (register1 != register2) { - Manager::getInstance()->setStreamPosition(streamPos); + man.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 !!! // Speichert die Position des nächsten Befehls im angegebenen Register - const auto pos = static_cast(Manager::getInstance()->getNextStreamLineOffset().operator std::streamoff()); - Register::getInstance().setRegister(arg1, pos); + const auto pos = static_cast(man.getNextStreamLineOffset().operator std::streamoff()); + reg.setRegister(arg1, pos); // Holt sich die Position des angegebenen labels und die stream Position auf diese const auto &label = commandVector.at(2); - const auto streamPos = ProgramLoader::getInstance()->getStreamPosition(label); - Manager::getInstance()->setStreamPosition(streamPos); + const auto streamPos = pl.getStreamPosition(label); + man.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 !!! // Speichert die Position des nächsten Befehls im angegebenen Register - const auto pos = static_cast(Manager::getInstance()->getNextStreamLineOffset().operator std::streamoff()); - Register::getInstance().setRegister(arg1, pos); + const auto pos = static_cast(man.getNextStreamLineOffset().operator std::streamoff()); + reg.setRegister(arg1, pos); // Holt sich die Position des angegebenen labels und das Offset aus dem angegebenen Register const auto &label = commandVector.at(3); - const auto streamPosLabel = ProgramLoader::getInstance()->getStreamPosition(label); - const auto posOffset = Register::getInstance().getRegister(arg2); + const auto streamPosLabel = pl.getStreamPosition(label); + const auto posOffset = reg.getRegister(arg2); // Setzt die stream Position auf den gegebenen Wert & erhöht sie um den angegebenen Offset - Manager::getInstance()->setStreamPosition(streamPosLabel); + man.setStreamPosition(streamPosLabel); for (int i = 0; i < posOffset; ++i) { - Manager::getInstance()->gotoNextStreamLine(); + man.gotoNextStreamLine(); } + } else { + std::cerr << "Befehl " << command << " wurde nicht gefunden!"; } } @@ -188,6 +199,7 @@ bool Alu::isRegister(const std::string &argument) { // 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; @@ -199,6 +211,7 @@ bool Alu::isRegister(const std::string &argument) { 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) { diff --git a/src/components/Memory.cpp b/src/components/Memory.cpp index e85be16..c2e1938 100644 --- a/src/components/Memory.cpp +++ b/src/components/Memory.cpp @@ -8,9 +8,9 @@ Memory::Memory() { m_memory = {0}; } -Memory *Memory::getInstance() { +Memory &Memory::getInstance() { static Memory instance; - return &instance; + return instance; } void Memory::store(const int address, const int value) { diff --git a/src/components/Memory.h b/src/components/Memory.h index 5a8d737..94d743b 100644 --- a/src/components/Memory.h +++ b/src/components/Memory.h @@ -35,7 +35,7 @@ public: * * @return Memory Instanz */ - static Memory *getInstance(); + static Memory &getInstance(); /** * Speichert den angegebenen Wert an der angegebenen Adresse im Hauptspeicher diff --git a/src/main.cpp b/src/main.cpp index 1689dc8..de95200 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,9 +5,9 @@ #include "Manager.h" int main() { - Manager::getInstance()->init( + Manager::getInstance().init( "/home/black/Nextcloud/Dokumente/Dokumente/Hochschule/2. Semester/Rechnerarchitektur/Projekt/test.txt" ); - Manager::getInstance()->run(); + Manager::getInstance().run(); return 0; }