diff --git a/7/CMakeLists.txt b/7/CMakeLists.txt new file mode 100644 index 0000000..4310ae6 --- /dev/null +++ b/7/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.29) +project(syspro_cpp) + +set(CMAKE_CXX_STANDARD 17) + +add_executable(syspro_cpp + src/main.cpp + src/emulator.cpp + src/Jmpz.cpp + src/Mov.cpp + src/Add.cpp + src/Sub.cpp + src/Mul.cpp + src/Div.cpp + src/Load.cpp + src/Store.cpp + src/Jmp.cpp) + +target_include_directories(syspro_cpp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/7/README.md b/7/README.md new file mode 100644 index 0000000..de1d327 --- /dev/null +++ b/7/README.md @@ -0,0 +1,32 @@ +Here is an simple Very Reduced Instruction Set Computer (vrisc) architecture. +It has only four integer registers `R0`, `R1`, `R2`, `R3`. And also has `1024` RAM memory cells. + +The instruction set of vrisc assembly is: + +| Instruction | Semantics | +| ----------- | --------- | +| `Mov Ri Rj` | `Ri = Rj` | +| `Mov Ri x ` | `Ri = x` | +| `Add Ri Rj` | `Ri += Rj` | +| `Add Ri x` | `Ri += x` | +| `Sub Ri Rj` | `Ri -= Rj` | +| `Sub Ri x` | `Ri -= x` | +| `Mul Ri Rj` | `Ri *= Rj` | +| `Mul Ri x` | `Ri *= x` | +| `Div Ri Rj` | `Ri /= Rj` | +| `Div Ri x` | `Ri /= x` | +| `Load Ri x` | `Ri = Mem[x]` | +| `Store Ri x` | `Mem[x] = Ri` | +| `Jmp x` | `pc = x` | +| `Jmpz x` | `pc = if R0 == 0 then pc = x else pc++` | + +Where `x` is an immediate. + +Your task is to write a simple emulator of this architecture. +The emulator must be implemented in the OOP paradigm. +In file `emulator.cpp` you can find the template for your solution. +File contains the base class of all instructions `Instruction`. All other instructions should be derived from it. +At the end of the emulation emulator should return value on the `R0` register. +Feel free to modify this source. + +Don't forget about tests! diff --git a/7/factorial.vrisc b/7/factorial.vrisc new file mode 100644 index 0000000..ba2f179 --- /dev/null +++ b/7/factorial.vrisc @@ -0,0 +1,10 @@ +Mov R0 5 +Mov R1 1 +Mov Jmpz 6 + +Mul R1 R0 +Sub R0 1 +Jmp 2 + +Mov R0 R1 + diff --git a/7/include/Add.h b/7/include/Add.h new file mode 100644 index 0000000..3c9a944 --- /dev/null +++ b/7/include/Add.h @@ -0,0 +1,15 @@ +#pragma once +#include "emulator.h" + +namespace Emulator { + class Add : public Instruction { + Reg dst, src_reg; + int src_imm; + bool is_immediate; + + public: + Add(Reg dst, Reg src); + Add(Reg dst, int imm); + void eval(EmulatorState& emul) override; + }; +} diff --git a/7/include/Div.h b/7/include/Div.h new file mode 100644 index 0000000..7befcf5 --- /dev/null +++ b/7/include/Div.h @@ -0,0 +1,15 @@ +#pragma once +#include "emulator.h" + +namespace Emulator { + class Div : public Instruction { + Reg dst, src_reg; + int src_imm; + bool is_immediate; + + public: + Div(Reg dst, Reg src); + Div(Reg dst, int imm); + void eval(EmulatorState& emul) override; + }; +} \ No newline at end of file diff --git a/7/include/Jmp.h b/7/include/Jmp.h new file mode 100644 index 0000000..e5e855d --- /dev/null +++ b/7/include/Jmp.h @@ -0,0 +1,12 @@ +#pragma once +#include "emulator.h" + +namespace Emulator { + class Jmp : public Instruction { + int src; + + public: + explicit Jmp(int src); + void eval(EmulatorState& emul) override; + }; +} diff --git a/7/include/Jmpz.h b/7/include/Jmpz.h new file mode 100644 index 0000000..38c26f6 --- /dev/null +++ b/7/include/Jmpz.h @@ -0,0 +1,12 @@ +#pragma once +#include "emulator.h" + +namespace Emulator { + class Jmpz : public Instruction { + int src; + + public: + explicit Jmpz(int src); + void eval(EmulatorState& emul) override; + }; +} diff --git a/7/include/Load.h b/7/include/Load.h new file mode 100644 index 0000000..50fd20b --- /dev/null +++ b/7/include/Load.h @@ -0,0 +1,13 @@ +#pragma once +#include "emulator.h" + +namespace Emulator { + class Load : public Instruction { + Reg dst; + int src; + + public: + Load(Reg dst, int src); + void eval(EmulatorState& emul) override; + }; +} diff --git a/7/include/Mov.h b/7/include/Mov.h new file mode 100644 index 0000000..529e7bf --- /dev/null +++ b/7/include/Mov.h @@ -0,0 +1,15 @@ +#pragma once +#include "emulator.h" + +namespace Emulator { + class Mov : public Instruction { + Reg dst, src_reg; + int src_imm; + bool is_immediate; + + public: + Mov(Reg dst, Reg src); + Mov(Reg dst, int imm); + void eval(EmulatorState& emul) override; + }; +} \ No newline at end of file diff --git a/7/include/Mul.h b/7/include/Mul.h new file mode 100644 index 0000000..ef0045f --- /dev/null +++ b/7/include/Mul.h @@ -0,0 +1,15 @@ +#pragma once +#include "emulator.h" + +namespace Emulator { + class Mul : public Instruction { + Reg dst, src_reg; + int src_imm; + bool is_immediate; + + public: + Mul(Reg dst, Reg src); + Mul(Reg dst, int imm); + void eval(EmulatorState& emul) override; + }; +} diff --git a/7/include/Store.h b/7/include/Store.h new file mode 100644 index 0000000..3d138e6 --- /dev/null +++ b/7/include/Store.h @@ -0,0 +1,13 @@ +#pragma once +#include "emulator.h" + +namespace Emulator { + class Store : public Instruction { + int dst; + Reg src; + + public: + Store(Reg src, int dst); + void eval(EmulatorState& emul) override; + }; +} diff --git a/7/include/Sub.h b/7/include/Sub.h new file mode 100644 index 0000000..7d1ab61 --- /dev/null +++ b/7/include/Sub.h @@ -0,0 +1,15 @@ +#pragma once +#include "emulator.h" + +namespace Emulator { + class Sub : public Instruction { + Reg dst, src_reg; + int src_imm; + bool is_immediate; + + public: + Sub(Reg dst, Reg src); + Sub(Reg dst, int imm); + void eval(EmulatorState& emul) override; + }; +} diff --git a/7/include/emulator.h b/7/include/emulator.h new file mode 100644 index 0000000..fa27d92 --- /dev/null +++ b/7/include/emulator.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include + +namespace Emulator { + + enum Reg { + R0, R1, R2, R3 + }; + + struct EmulatorState { + static const size_t regs_size = 4; + static const size_t mem_size = 1024; + + std::vector _registers{regs_size}; + std::vector _mem{mem_size}; + + size_t _pc = 0; + }; + + class Instruction { + public: + virtual void eval(EmulatorState& emul) = 0; + virtual ~Instruction() = default; + }; + + std::optional strtoreg(const std::string& str); + std::vector parse(const std::string& program); + int emulate(const std::string& program_text); +} + +std::vector split(const std::string& s, char delim); +std::optional readStringFromFile(const std::string& filename); diff --git a/7/src/Add.cpp b/7/src/Add.cpp new file mode 100644 index 0000000..fd8bd58 --- /dev/null +++ b/7/src/Add.cpp @@ -0,0 +1,14 @@ +#include "Add.h" + +namespace Emulator { + Add::Add(Reg dst, Reg src) : dst(dst), src_reg(src), src_imm(0), is_immediate(false) {} + Add::Add(Reg dst, int imm) : dst(dst), src_reg(R0), src_imm(imm), is_immediate(true) {} + + void Add::eval(EmulatorState& emul) { + if (is_immediate) { + emul._registers[dst] += src_imm; + } else { + emul._registers[dst] += emul._registers[src_reg]; + } + } +} \ No newline at end of file diff --git a/7/src/Div.cpp b/7/src/Div.cpp new file mode 100644 index 0000000..3afdcf6 --- /dev/null +++ b/7/src/Div.cpp @@ -0,0 +1,14 @@ +#include "Div.h" + +namespace Emulator { + Div::Div(Reg dst, Reg src) : dst(dst), src_reg(src), src_imm(0), is_immediate(false) {} + Div::Div(Reg dst, int imm) : dst(dst), src_reg(R0), src_imm(imm), is_immediate(true) {} + + void Div::eval(EmulatorState& emul) { + if (is_immediate) { + emul._registers[dst] /= src_imm; + } else { + emul._registers[dst] /= emul._registers[src_reg]; + } + } +} \ No newline at end of file diff --git a/7/src/Jmp.cpp b/7/src/Jmp.cpp new file mode 100644 index 0000000..017a819 --- /dev/null +++ b/7/src/Jmp.cpp @@ -0,0 +1,9 @@ +#include "Jmp.h" + +namespace Emulator { + Jmp::Jmp(int src) : src(src) {} + + void Jmp::eval(EmulatorState& emul) { + emul._pc = src; + } +} \ No newline at end of file diff --git a/7/src/Jmpz.cpp b/7/src/Jmpz.cpp new file mode 100644 index 0000000..a254bf4 --- /dev/null +++ b/7/src/Jmpz.cpp @@ -0,0 +1,9 @@ +#include "Jmpz.h" + +namespace Emulator { + Jmpz::Jmpz(int src) : src(src) {} + + void Jmpz::eval(EmulatorState& emul) { + emul._pc = (emul._registers[R0] == 0) ? src : (emul._pc + 1); + } +} \ No newline at end of file diff --git a/7/src/Load.cpp b/7/src/Load.cpp new file mode 100644 index 0000000..aacee44 --- /dev/null +++ b/7/src/Load.cpp @@ -0,0 +1,9 @@ +#include "Load.h" + +namespace Emulator { + Load::Load(Reg dst, int src) : dst(dst), src(src) {} + + void Load::eval(EmulatorState& emul) { + emul._registers[dst] = emul._mem[src]; + } +} \ No newline at end of file diff --git a/7/src/Mov.cpp b/7/src/Mov.cpp new file mode 100644 index 0000000..a3691ec --- /dev/null +++ b/7/src/Mov.cpp @@ -0,0 +1,14 @@ +#include "Mov.h" + +namespace Emulator { + Mov::Mov(Reg dst, Reg src) : dst(dst), src_reg(src), src_imm(0), is_immediate(false) {} + Mov::Mov(Reg dst, int imm) : dst(dst), src_reg(R0), src_imm(imm), is_immediate(true) {} + + void Mov::eval(EmulatorState& emul) { + if (is_immediate) { + emul._registers[dst] = src_imm; + } else { + emul._registers[dst] = emul._registers[src_reg]; + } + } +} \ No newline at end of file diff --git a/7/src/Mul.cpp b/7/src/Mul.cpp new file mode 100644 index 0000000..3b60752 --- /dev/null +++ b/7/src/Mul.cpp @@ -0,0 +1,14 @@ +#include "Mul.h" + +namespace Emulator { + Mul::Mul(Reg dst, Reg src) : dst(dst), src_reg(src), src_imm(0), is_immediate(false) {} + Mul::Mul(Reg dst, int imm) : dst(dst), src_reg(R0), src_imm(imm), is_immediate(true) {} + + void Mul::eval(EmulatorState& emul) { + if (is_immediate) { + emul._registers[dst] *= src_imm; + } else { + emul._registers[dst] *= emul._registers[src_reg]; + } + } +} \ No newline at end of file diff --git a/7/src/Store.cpp b/7/src/Store.cpp new file mode 100644 index 0000000..1cd0cba --- /dev/null +++ b/7/src/Store.cpp @@ -0,0 +1,9 @@ +#include "Store.h" + +namespace Emulator { + Store::Store(Reg src, int dst) : dst(dst), src(src) {} + + void Store::eval(EmulatorState& emul) { + emul._mem[dst] = emul._registers[src]; + } +} \ No newline at end of file diff --git a/7/src/Sub.cpp b/7/src/Sub.cpp new file mode 100644 index 0000000..84224d6 --- /dev/null +++ b/7/src/Sub.cpp @@ -0,0 +1,14 @@ +#include "Sub.h" + +namespace Emulator { + Sub::Sub(Reg dst, Reg src) : dst(dst), src_reg(src), src_imm(0), is_immediate(false) {} + Sub::Sub(Reg dst, int imm) : dst(dst), src_reg(R0), src_imm(imm), is_immediate(true) {} + + void Sub::eval(EmulatorState& emul) { + if (is_immediate) { + emul._registers[dst] -= src_imm; + } else { + emul._registers[dst] -= emul._registers[src_reg]; + } + } +} \ No newline at end of file diff --git a/7/src/emulator.cpp b/7/src/emulator.cpp new file mode 100644 index 0000000..4a29935 --- /dev/null +++ b/7/src/emulator.cpp @@ -0,0 +1,119 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "emulator.h" +#include "Add.h" +#include "Div.h" +#include "Jmp.h" +#include "Jmpz.h" +#include "Load.h" +#include "Mov.h" +#include "Mul.h" +#include "Store.h" +#include "Sub.h" + +std::vector split(const std::string& s, char delim) { + std::stringstream ss(s); + std::string a; + std::vector res; + while (std::getline(ss, a, delim)) { + res.push_back(a); + } + return res; +} + +std::optional readStringFromFile(const std::string& filename) { + std::ifstream file{filename}; + if (!file) return {}; + std::stringstream buf; + buf << file.rdbuf(); + return buf.str(); +} + +namespace Emulator { + std::optional strtoreg(const std::string& str) { + if (str == "R0") return R0; + if (str == "R1") return R1; + if (str == "R2") return R2; + if (str == "R3") return R3; + return std::nullopt; + } + + template + void parseTwoOperandInstruction(std::vector& words, std::vector& instructions) { + auto dst = strtoreg(words[1]); + if (!dst.has_value()) { + throw std::invalid_argument("Invalid destination register"); + } + if (auto src = strtoreg(words[2]); src.has_value()) { + instructions.push_back(new OpClass(dst.value(), src.value())); + } else { + try { + int imm = stoi(words[2]); + instructions.push_back(new OpClass(dst.value(), imm)); + } catch (const std::invalid_argument& e) { + std::cerr << "Invalid immediate: " << words[2] << std::endl; + } + } + } + + std::vector parse(const std::string& program) { + std::vector instructions{}; + std::vector lines = split(program, '\n'); + + for (int i = 0; i < lines.size(); i++) { + if (lines[i].empty()) continue; + + std::vector words = split(lines[i], ' '); + if (words.empty()) continue; + + const std::string& op = words[0]; + if (op == "Mov") parseTwoOperandInstruction(words, instructions); + else if (op == "Add") parseTwoOperandInstruction(words, instructions); + else if (op == "Sub") parseTwoOperandInstruction(words, instructions); + else if (op == "Mul") parseTwoOperandInstruction(words, instructions); + else if (op == "Div") parseTwoOperandInstruction
(words, instructions); + else if (op == "Load") parseTwoOperandInstruction(words, instructions); + else if (op == "Store") parseTwoOperandInstruction(words, instructions); + else if (op == "Jmp") { + try { + instructions.push_back(new Jmp(std::stoi(words[1]))); + } catch (const std::invalid_argument& e) { + std::cerr << "Invalid immediate: " << words[1] << std::endl; + } + } + else if (op == "Jmpz") { + try { + instructions.push_back(new Jmpz(std::stoi(words[1]))); + } catch (const std::invalid_argument& e) { + std::cerr << "Invalid immediate: " << words[1] << std::endl; + } + } + } + return instructions; + } + + int emulate(const std::string& program_text) { + std::vector program = parse(program_text); + EmulatorState state; + + while (state._pc < program.size()) { + size_t current_pc = state._pc; + program[current_pc]->eval(state); + if (state._pc == current_pc) { + state._pc++; + } + } + + for (size_t i = 0; i < program.size(); i++) { + delete program[i]; + } + + return state._registers[R0]; + } +} \ No newline at end of file diff --git a/7/src/main.cpp b/7/src/main.cpp new file mode 100644 index 0000000..bb75b82 --- /dev/null +++ b/7/src/main.cpp @@ -0,0 +1,33 @@ +#include +#include +#include +#include "emulator.h" + +int main() { + std::string factorial = + "Mov R0 5\n" + "Mov R1 1\n" + "Jmpz 6\n" + "Mul R1 R0\n" + "Sub R0 1\n" + "Jmp 2\n" + "Mov R0 R1"; + assert(Emulator::emulate(factorial) == 120); + + std::string math_test = + "Mov R0 10\n" + "Add R0 5\n" + "Sub R0 2\n" + "Mul R0 2\n" + "Div R0 2"; + assert(Emulator::emulate(math_test) == 13); + + std::string mem_test = + "Mov R0 42\n" + "Store R0 100\n" + "Mov R0 0\n" + "Load R0 100"; + assert(Emulator::emulate(mem_test) == 42); + + return 0; +} \ No newline at end of file