diff --git a/README.md b/README.md index fbb6fc5..1a2a6d2 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Es gibt folgende Anweisungen: | Befehl | Abkürzung für | Aktion | - |:------:|:-------------:|:-------------------------------------------:| + |:------:|:-------------:|:-------------------------------------------:| | s | sprint | Führt den Sourcecode bis zum Ende aus | | l | line | Führe die nächste Zeile des Sourcecodes aus | | e | end | Beende den Emulator direkt | @@ -62,6 +62,10 @@ - Leerzeilen sind erlaubt. - Kommentare sind mit `#` beginnend, auch inline, erlaubt. - Der Assemblycode muss in einer `.txt` Datei gespeichert werden. +- Speziell für Beispielprogramm 8: In diesem wird in Zeile 12 der Wert des Pointers auf das Array um 4 erhöht, um somit + zum nächsten Element zu springen. Aufgrund der Funktionsweise dieses Emulators ist das nicht nötig und der Wert + braucht + lediglich um 1 erhöht zu werden. In den beigefügten Beispielprogrammen ist das bereits berücksichtigt. ## Funktionsweise diff --git a/beispielprogramme/8.txt b/beispielprogramme/8.txt index 6210fde..589049e 100644 --- a/beispielprogramme/8.txt +++ b/beispielprogramme/8.txt @@ -9,7 +9,7 @@ addi x4, x1, 0 # x4 = Laufender Zeiger (ptr) ins Array lw x3, 0(x4) # x3 = erstes Element (initiales Maximum) addi x2, x2, −1 # x2 = verbleibende Elemente loop_max: -addi x4, x4, 4 # ptr += 4 (naechstes Element ) +addi x4, x4, 1 # ptr += 4 (naechstes Element ) lw x5, 0(x4) # x5 = aktuelles Element slt x6, x3, x5 # x6 = 1 , wenn x3 < x5 bne x6, x0, update # falls neues Element groesser: update diff --git a/src/components/Alu.cpp b/src/components/Alu.cpp index e5eccb3..4bfdf48 100644 --- a/src/components/Alu.cpp +++ b/src/components/Alu.cpp @@ -28,7 +28,8 @@ void Alu::calculate(const std::vector &commandVector) { const std::string &command = commandVector.at(0); // Extrahiere die Argumente des Befehls - const auto arg1 = parseArgument(commandVector.at(1)); + int arg1 = 0; + if (isRegister(commandVector.at(1))) arg1 = parseArgument(commandVector.at(1)); // Wandle Vektor[2] (das zweite Argument) in eine Adresse um, falls es ein SW/LW Argument ist int arg2 = 0; @@ -44,7 +45,8 @@ void Alu::calculate(const std::vector &commandVector) { // 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)) { + const std::string &arg = commandVector.at(3); + if (isRegister(arg) || isImmediate(arg)) { arg3 = parseArgument(arg); } } @@ -118,6 +120,13 @@ void Alu::calculate(const std::vector &commandVector) { // Hole die Adresse aus dem Befehlsargument und die dazugehörigen Daten aus dem RAM const auto address = arg2; const auto data = mem.load(address); + if (data == -1) { + std::cerr << "Es wurde ein Speicherzugriff außerhalb des maximal erlaubten Speicherbereiches versucht: " + << address << "\nDer Fehler ist in folgender Zeile aufgetreten: " << commandVector.at(0) << " " + << commandVector.at(1) << " " << commandVector.at(2) << "\nDer Emulator beendet nun!\n" << + std::flush; + exit(1); + } // Speichere die Daten im angegeben Register reg.setRegister(arg1, data); } else if (command == "sw") { @@ -148,7 +157,7 @@ void Alu::calculate(const std::vector &commandVector) { // !!! 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(man.getNextStreamLineOffset().operator std::streamoff()); + const auto pos = static_cast(man.getStreamPosition().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); @@ -159,12 +168,10 @@ void Alu::calculate(const std::vector &commandVector) { // !!! 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 nextStreamLine = static_cast(man.getNextStreamLineOffset().operator std::streamoff()); - reg.setRegister(arg1, nextStreamLine); + const auto currentStreamLine = static_cast(man.getStreamPosition().operator std::streamoff()); + reg.setRegister(arg1, currentStreamLine); // Erhöht die stream position (PC) um den angegebenen Wert - for (int i = 1; i < arg2; ++i) { - man.gotoNextStreamLine(); - } + man.setStreamPosition(arg2); } else if (command == "j") { // Jump Befehl // Springe zum angegebenen Label @@ -192,6 +199,18 @@ 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 + // Falls das '-' noch in Unicode ist, wandle es in ASCII um + if (argument.size() >= 3) { + const auto b0 = static_cast(argument[0]); + const auto b1 = static_cast(argument[1]); + const auto b2 = static_cast(argument[2]); + if (b0 == 226 && b1 == 136 && b2 == 146) { + if (argument.size() == 3) return false; // Nur Minuszeichen, keine Zahl + // Ersetze Unicode-Minus durch ASCII-Minus und wandle den Rest um + const std::string fixedArg = "-" + argument.substr(3); + return std::stoi(fixedArg); + } + } return std::stoi(argument); } @@ -228,10 +247,30 @@ 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 + size_t start = 0; + + // Erlaube optional ein Minuszeichen am Anfang + // In Beispielcode 8 wird ein Unicode-Minus verwendet. Daher wird sicherheitshalber für beide Versionen gecheckt + // Prüfe auf ASCII-Minus + if (argument[0] == '-') { + if (argument.size() == 1) return false; + start = 1; + } + // Prüfe auf Unicode-Minus (dreibyte UTF-8) + else if (argument.size() >= 3) { + const auto b0 = static_cast(argument[0]); + const auto b1 = static_cast(argument[1]); + const auto b2 = static_cast(argument[2]); + if (b0 == 226 && b1 == 136 && b2 == 146) { + if (argument.size() == 3) return false; + // Minuszeichen belegt dann 3 Bytes + start = 3; + } + } + + // Prüfe, ob jedes Zeichen ab start eine Ziffer ist for (size_t i = start; i < argument.size(); ++i) { - if (!std::isdigit(argument.at(i))) return false; + if (!std::isdigit(argument[i])) return false; } return true; } diff --git a/src/components/Memory.cpp b/src/components/Memory.cpp index 40dc0cb..dd486c8 100644 --- a/src/components/Memory.cpp +++ b/src/components/Memory.cpp @@ -21,6 +21,9 @@ void Memory::store(const int address, const int value) { } int Memory::load(const int address) const { + if (address >= m_memory.size()) { + return -1; + } return m_memory.at(address); } diff --git a/src/components/Register.cpp b/src/components/Register.cpp index 12d0bfb..5bb6a35 100644 --- a/src/components/Register.cpp +++ b/src/components/Register.cpp @@ -22,7 +22,7 @@ void Register::setRegister(const int reg, const int value) { std::cerr << "\nRegister 0 darf nicht beschrieben werden! Das geschriebene Register wird " "auf 0 gesetzt. Dabei handelt es sich nicht unbedingt um einen Assembly " "Fehler, \nes ist in den meisten Fällen nur dem Emulator Aufbau geschuldet. (Für den Beispielcode " - "4 z.B. ist dieser Fehler unbedenklich, es kann fortgefahren werden." + "4 z.B. ist dieser Fehler unbedenklich, es kann fortgefahren werden.)" "\n" << std::flush; m_registers[reg] = 0; return;