Files
riscv-emulator/src/Manager.cpp
2025-07-09 22:13:59 +02:00

194 lines
6.9 KiB
C++

//
// Created by black on 12.06.25.
//
#include "Manager.h"
#include <iostream>
#include <filesystem>
#include "components/Alu.h"
#include "components/Memory.h"
#include "components/Register.h"
namespace fs = std::filesystem;
Manager::~Manager() {
std::cout << "Schließe Quellcode Datei: '" << m_path << "'" << "\n" << std::flush;
m_programFile.close();
}
Manager &Manager::getInstance() {
static Manager instance;
return instance;
}
[[noreturn]] void Manager::run() {
if (!m_programFile.is_open()) {
std::cerr << "Bitte zuerst die init() Methode aufrufen!" << "\n" << std::flush;
return;
}
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);
bool runAll = false;
// 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);
// Frage nach und reagiere auf Nutzereingaben
if (!runAll) {
std::cout << "\n" << "Bearbeitete Codezeile: \033[34m" << line << "\033[0m";
runAll = handleUserInput();
}
}
// Beende das Program erst, wenn der Nutzer das tut
std::cout <<
"\nDer Quellcode ist nun \033[33mfertig\033[0m bearbeitet. Sie können noch auf den RAM und die Register zugreifen.";
while (true) handleExitInput();
}
void Manager::setStreamPosition(const std::streampos pos) {
m_programFile.seekg(pos);
}
std::streampos Manager::getStreamPosition() {
return m_programFile.tellg();
}
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;
std::cerr << "bad (schwerwiegender Fehler): " << m_programFile.bad() << "\n" << std::flush;
return;
}
constexpr auto COLOR_GREEN = "\033[32m";
constexpr auto COLOR_YELLOW = "\033[33m";
constexpr auto COLOR_BLUE = "\033[34m";
constexpr auto COLOR_RESET = "\033[0m";
std::cout << COLOR_GREEN << "=== Herzlich willkommen beim RISC-V Emulator! ===" << COLOR_RESET << "\n";
std::cout << "Nachfolgend wird nun die angegebene Quellcodedatei eingelesen und von der ALU bearbeitet.\n"
<< COLOR_YELLOW << "Nach" << COLOR_RESET << " jeder bearbeiteten Codezeile wird "
<< COLOR_BLUE << "diese" << COLOR_RESET << " ausgegeben und Sie werden "
<< "nach dem " << COLOR_GREEN << "nächsten Schritt" << COLOR_RESET <<
" gefragt. Tätigen Sie Ihre Eingabe und bestätigen Sie mit "
<< COLOR_GREEN << "ENTER" << COLOR_RESET <<
".\nAnforderungen und Hinweise entnehmen Sie bitte der README.\n\n";
}
std::streampos Manager::getNextStreamLineOffset() {
// 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
gotoNextStreamLine();
const auto positionAfter = m_programFile.tellg();
// Geht zum offset vom Anfang zurück und gibt die nächste Adresse (PC+4) zurück
m_programFile.seekg(positionBefore);
return positionAfter;
}
std::string Manager::gotoNextStreamLine() {
std::string line;
do {
getline(m_programFile, line);
} while (line.empty());
return line;
}
bool Manager::handleUserInput() {
constexpr auto COLOR_GREEN = "\033[32m";
constexpr auto COLOR_RESET = "\033[0m";
std::cout << "\nOptionen: ("
<< COLOR_GREEN << "s" << COLOR_RESET << ") Programm durchlaufen lassen, ("
<< COLOR_GREEN << "l" << COLOR_RESET << ") nächste Zeile, ("
<< COLOR_GREEN << "e" << COLOR_RESET << ") beenden, ("
<< COLOR_GREEN << "m" << COLOR_RESET << ") Memory Dump, ("
<< COLOR_GREEN << "r" << COLOR_RESET << ") Register Dump\n";
char input = '\0';
while (true) {
std::cout << "Eingabe: ";
std::cin >> input;
// ignoriere die Groß- und Kleinschreibung
input = std::tolower(input);
if (input == 's') {
return true;
} else if (input == 'l') {
return false;
} else if (input == 'e') {
std::cout << "Programm wird beendet.\n";
exit(0);
} else if (input == 'm') {
std::cout << "Wie viele Speicherzellen sollen gedumpt werden? ";
int dumpSize = 0;
while (!(std::cin >> dumpSize) || dumpSize == 0) {
std::cout << "Ungültige Eingabe. Bitte eine positive Zahl eingeben: ";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Memory::getInstance().dump(dumpSize);
} else if (input == 'r') {
Register::getInstance().dump();
} else {
std::cout << "Ungültige Eingabe. Bitte erneut versuchen.\n";
}
}
}
void Manager::handleExitInput() {
constexpr auto COLOR_GREEN = "\033[32m";
constexpr auto COLOR_RESET = "\033[0m";
std::cout << "\nOptionen: ("
<< COLOR_GREEN << "e" << COLOR_RESET << ") beenden, ("
<< COLOR_GREEN << "m" << COLOR_RESET << ") Memory Dump, ("
<< COLOR_GREEN << "r" << COLOR_RESET << ") Register Dump\n";
char input = '\0';
while (true) {
std::cout << "Eingabe: ";
std::cin >> input;
input = std::tolower(input);
if (input == 'e') {
std::cout << "Programm wird beendet. :pepeExit:\n";
exit(0); // oder andere Beendigung
} else if (input == 'm') {
std::cout << "Wie viele Speicherzellen sollen gedumpt werden? ";
int dumpSize = 0;
while (!(std::cin >> dumpSize) || dumpSize == 0) {
std::cout << "Ungültige Eingabe. Bitte eine positive Zahl eingeben: ";
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Memory::getInstance().dump(dumpSize);
} else if (input == 'r') {
Register::getInstance().dump();
} else {
std::cout << "Ungültige Eingabe. Bitte erneut versuchen.\n";
}
}
}