From 1c5ef2e74c57128051d084c139ea4f95b878d7b0 Mon Sep 17 00:00:00 2001 From: Ivan Ugliansky Date: Sat, 14 Mar 2026 08:39:36 +0700 Subject: [PATCH 1/3] Initial commit --- README.md | 32 ++++++++++++++ emulator.cpp | 111 ++++++++++++++++++++++++++++++++++++++++++++++++ factorial.vrisc | 10 +++++ 3 files changed, 153 insertions(+) create mode 100644 README.md create mode 100644 emulator.cpp create mode 100644 factorial.vrisc diff --git a/README.md b/README.md new file mode 100644 index 0000000..de1d327 --- /dev/null +++ b/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/emulator.cpp b/emulator.cpp new file mode 100644 index 0000000..2a7d435 --- /dev/null +++ b/emulator.cpp @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include +#include + +namespace Emulator { + enum Reg { + R0, R1, R2, R3 + }; + + class EmulatorState; + + // TODO: implement all instructions listed in ISA. This class should be base class for all insturctions + class Instruction { + public: + virtual void eval(EmulatorState& emul) = 0; + virtual ~Instruction() {}; + }; + + /* This function accepts the program written in the vrisc assembly + * If the input program is correct, returns sequence of insturction, corresponding to the input program. + * If the input text is incorrect, the behaviour is undefined + */ + std::vector parse(const std::string& program) { + // TODO: implement it! + // feel free to change signature of this function + return std::vector{}; + } + + 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; // program counter register + }; + + /* Emulate receive a program, written in the vrisc assembly, + * in case of the correct program, emulate returns R0 value at the end of the emulation. + * If the program is incorrect, that is, either its text is not vrisc assembly language or it contains UB(endless cycles), + * the behaviour of emulate if also undefined. Handle these cases in any way. + */ + int emulate(const std::string& program_text) { + // Feel free to change code of this function + std::vector program = parse(program_text); + + EmulatorState state; + + while (state._pc < program.size()) { + program[state._pc]->eval(state); + } + + for (size_t i = 0; i < program.size(); i++) { + delete program[i]; + } + + return state._registers[R0]; + } +} + +// Simple helper for file as single line. Feel free to change it or delete it completely +std::optional readStringFromFile(const std::string& filename) { + std::ifstream file{filename}; + + if (!file) return {}; + + std::stringstream buf; + buf << file.rdbuf(); + + return buf.str(); +} + +int main() { + // For writing test you can write programs directly in raw string literals + std::string factorial = R"( + Mov R0 5 + Mov R1 1 + Mov Jmpz 6 + + Mul R1 R0 + Sub R0 1 + Jmp 2 + + Mov R0 R1 + )"; + + // The result should be 120 + int fact5 = Emulator::emulate(factorial); + + + // Or you can use file IO + std::string filename = "factorial.vrisc"; + std::optional program = readStringFromFile("factorial.vrisc"); + + if (!program) { + std::cerr << "Can't open file" << std::endl; + return 1; + } + + // And this also should be 120 + int another_fact = Emulator::emulate(*program); + + // TODO: remeber the tests is very important! + + return 0; +} diff --git a/factorial.vrisc b/factorial.vrisc new file mode 100644 index 0000000..ba2f179 --- /dev/null +++ b/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 + From 98918ecefed510c8bfab191f88b7d3553e089ea4 Mon Sep 17 00:00:00 2001 From: Trinity-142 Date: Wed, 25 Mar 2026 20:51:48 +0700 Subject: [PATCH 2/3] solution --- 7/CMakeLists.txt | 19 ++++ README.md => 7/README.md | 0 factorial.vrisc => 7/factorial.vrisc | 0 7/include/Add.h | 15 +++ 7/include/Div.h | 15 +++ 7/include/Jmp.h | 12 +++ 7/include/Jmpz.h | 12 +++ 7/include/Load.h | 13 +++ 7/include/Mov.h | 15 +++ 7/include/Mul.h | 15 +++ 7/include/Store.h | 13 +++ 7/include/Sub.h | 15 +++ 7/include/emulator.h | 34 +++++++ 7/src/Add.cpp | 14 +++ 7/src/Div.cpp | 14 +++ 7/src/Jmp.cpp | 9 ++ 7/src/Jmpz.cpp | 9 ++ 7/src/Load.cpp | 9 ++ 7/src/Mov.cpp | 14 +++ 7/src/Mul.cpp | 14 +++ 7/src/Store.cpp | 9 ++ 7/src/Sub.cpp | 14 +++ 7/src/emulator.cpp | 137 +++++++++++++++++++++++++++ 7/src/main.cpp | 33 +++++++ emulator.cpp | 111 ---------------------- 25 files changed, 454 insertions(+), 111 deletions(-) create mode 100644 7/CMakeLists.txt rename README.md => 7/README.md (100%) rename factorial.vrisc => 7/factorial.vrisc (100%) create mode 100644 7/include/Add.h create mode 100644 7/include/Div.h create mode 100644 7/include/Jmp.h create mode 100644 7/include/Jmpz.h create mode 100644 7/include/Load.h create mode 100644 7/include/Mov.h create mode 100644 7/include/Mul.h create mode 100644 7/include/Store.h create mode 100644 7/include/Sub.h create mode 100644 7/include/emulator.h create mode 100644 7/src/Add.cpp create mode 100644 7/src/Div.cpp create mode 100644 7/src/Jmp.cpp create mode 100644 7/src/Jmpz.cpp create mode 100644 7/src/Load.cpp create mode 100644 7/src/Mov.cpp create mode 100644 7/src/Mul.cpp create mode 100644 7/src/Store.cpp create mode 100644 7/src/Sub.cpp create mode 100644 7/src/emulator.cpp create mode 100644 7/src/main.cpp delete mode 100644 emulator.cpp 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/README.md b/7/README.md similarity index 100% rename from README.md rename to 7/README.md diff --git a/factorial.vrisc b/7/factorial.vrisc similarity index 100% rename from factorial.vrisc rename to 7/factorial.vrisc 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..ced3a53 --- /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(int dst, Reg src); + 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..7fb9ebd --- /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; + }; + + Reg 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..5a06686 --- /dev/null +++ b/7/src/Store.cpp @@ -0,0 +1,9 @@ +#include "Store.h" + +namespace Emulator { + Store::Store(int dst, Reg src) : 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..5e80ef5 --- /dev/null +++ b/7/src/emulator.cpp @@ -0,0 +1,137 @@ +#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 { + Reg 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; + throw std::invalid_argument("invalid register"); + } + + 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") { + Reg dst = strtoreg(words[1]); + try { + Reg src = strtoreg(words[2]); + instructions.push_back(new Mov(dst, src)); + } catch (const std::invalid_argument& e) { + instructions.push_back(new Mov(dst, std::stoi(words[2]))); + } + } + else if (op == "Add") { + Reg dst = strtoreg(words[1]); + try { + Reg src = strtoreg(words[2]); + instructions.push_back(new Add(dst, src)); + } catch (const std::invalid_argument& e) { + instructions.push_back(new Add(dst, std::stoi(words[2]))); + } + } + else if (op == "Sub") { + Reg dst = strtoreg(words[1]); + try { + Reg src = strtoreg(words[2]); + instructions.push_back(new Sub(dst, src)); + } catch (const std::invalid_argument& e) { + instructions.push_back(new Sub(dst, std::stoi(words[2]))); + } + } + else if (op == "Mul") { + Reg dst = strtoreg(words[1]); + try { + Reg src = strtoreg(words[2]); + instructions.push_back(new Mul(dst, src)); + } catch (const std::invalid_argument& e) { + instructions.push_back(new Mul(dst, std::stoi(words[2]))); + } + } + else if (op == "Div") { + Reg dst = strtoreg(words[1]); + try { + Reg src = strtoreg(words[2]); + instructions.push_back(new Div(dst, src)); + } catch (const std::invalid_argument& e) { + instructions.push_back(new Div(dst, std::stoi(words[2]))); + } + } + else if (op == "Load") { + instructions.push_back(new Load(strtoreg(words[1]), std::stoi(words[2]))); + } + else if (op == "Store") { + instructions.push_back(new Store(std::stoi(words[2]), strtoreg(words[1]))); + } + else if (op == "Jmp") { + instructions.push_back(new Jmp(std::stoi(words[1]))); + } + else if (op == "Jmpz") { + instructions.push_back(new Jmpz(std::stoi(words[1]))); + } + } + 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[state._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 diff --git a/emulator.cpp b/emulator.cpp deleted file mode 100644 index 2a7d435..0000000 --- a/emulator.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -namespace Emulator { - enum Reg { - R0, R1, R2, R3 - }; - - class EmulatorState; - - // TODO: implement all instructions listed in ISA. This class should be base class for all insturctions - class Instruction { - public: - virtual void eval(EmulatorState& emul) = 0; - virtual ~Instruction() {}; - }; - - /* This function accepts the program written in the vrisc assembly - * If the input program is correct, returns sequence of insturction, corresponding to the input program. - * If the input text is incorrect, the behaviour is undefined - */ - std::vector parse(const std::string& program) { - // TODO: implement it! - // feel free to change signature of this function - return std::vector{}; - } - - 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; // program counter register - }; - - /* Emulate receive a program, written in the vrisc assembly, - * in case of the correct program, emulate returns R0 value at the end of the emulation. - * If the program is incorrect, that is, either its text is not vrisc assembly language or it contains UB(endless cycles), - * the behaviour of emulate if also undefined. Handle these cases in any way. - */ - int emulate(const std::string& program_text) { - // Feel free to change code of this function - std::vector program = parse(program_text); - - EmulatorState state; - - while (state._pc < program.size()) { - program[state._pc]->eval(state); - } - - for (size_t i = 0; i < program.size(); i++) { - delete program[i]; - } - - return state._registers[R0]; - } -} - -// Simple helper for file as single line. Feel free to change it or delete it completely -std::optional readStringFromFile(const std::string& filename) { - std::ifstream file{filename}; - - if (!file) return {}; - - std::stringstream buf; - buf << file.rdbuf(); - - return buf.str(); -} - -int main() { - // For writing test you can write programs directly in raw string literals - std::string factorial = R"( - Mov R0 5 - Mov R1 1 - Mov Jmpz 6 - - Mul R1 R0 - Sub R0 1 - Jmp 2 - - Mov R0 R1 - )"; - - // The result should be 120 - int fact5 = Emulator::emulate(factorial); - - - // Or you can use file IO - std::string filename = "factorial.vrisc"; - std::optional program = readStringFromFile("factorial.vrisc"); - - if (!program) { - std::cerr << "Can't open file" << std::endl; - return 1; - } - - // And this also should be 120 - int another_fact = Emulator::emulate(*program); - - // TODO: remeber the tests is very important! - - return 0; -} From 4765b06037327c004af786b163810f8afd945814 Mon Sep 17 00:00:00 2001 From: Trinity-142 Date: Fri, 22 May 2026 10:20:05 +0700 Subject: [PATCH 3/3] fix try-catch for control flow --- 7/include/Store.h | 2 +- 7/include/emulator.h | 2 +- 7/src/Store.cpp | 2 +- 7/src/emulator.cpp | 86 ++++++++++++++++++-------------------------- 4 files changed, 37 insertions(+), 55 deletions(-) diff --git a/7/include/Store.h b/7/include/Store.h index ced3a53..3d138e6 100644 --- a/7/include/Store.h +++ b/7/include/Store.h @@ -7,7 +7,7 @@ namespace Emulator { Reg src; public: - Store(int dst, Reg src); + Store(Reg src, int dst); void eval(EmulatorState& emul) override; }; } diff --git a/7/include/emulator.h b/7/include/emulator.h index 7fb9ebd..fa27d92 100644 --- a/7/include/emulator.h +++ b/7/include/emulator.h @@ -25,7 +25,7 @@ namespace Emulator { virtual ~Instruction() = default; }; - Reg strtoreg(const std::string& str); + std::optional strtoreg(const std::string& str); std::vector parse(const std::string& program); int emulate(const std::string& program_text); } diff --git a/7/src/Store.cpp b/7/src/Store.cpp index 5a06686..1cd0cba 100644 --- a/7/src/Store.cpp +++ b/7/src/Store.cpp @@ -1,7 +1,7 @@ #include "Store.h" namespace Emulator { - Store::Store(int dst, Reg src) : dst(dst), src(src) {} + Store::Store(Reg src, int dst) : dst(dst), src(src) {} void Store::eval(EmulatorState& emul) { emul._mem[dst] = emul._registers[src]; diff --git a/7/src/emulator.cpp b/7/src/emulator.cpp index 5e80ef5..4a29935 100644 --- a/7/src/emulator.cpp +++ b/7/src/emulator.cpp @@ -36,12 +36,30 @@ std::optional readStringFromFile(const std::string& filename) { } namespace Emulator { - Reg strtoreg(const std::string& str) { + 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; - throw std::invalid_argument("invalid register"); + 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) { @@ -55,63 +73,27 @@ namespace Emulator { if (words.empty()) continue; const std::string& op = words[0]; - if (op == "Mov") { - Reg dst = strtoreg(words[1]); - try { - Reg src = strtoreg(words[2]); - instructions.push_back(new Mov(dst, src)); - } catch (const std::invalid_argument& e) { - instructions.push_back(new Mov(dst, std::stoi(words[2]))); - } - } - else if (op == "Add") { - Reg dst = strtoreg(words[1]); - try { - Reg src = strtoreg(words[2]); - instructions.push_back(new Add(dst, src)); - } catch (const std::invalid_argument& e) { - instructions.push_back(new Add(dst, std::stoi(words[2]))); - } - } - else if (op == "Sub") { - Reg dst = strtoreg(words[1]); - try { - Reg src = strtoreg(words[2]); - instructions.push_back(new Sub(dst, src)); - } catch (const std::invalid_argument& e) { - instructions.push_back(new Sub(dst, std::stoi(words[2]))); - } - } - else if (op == "Mul") { - Reg dst = strtoreg(words[1]); + 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 { - Reg src = strtoreg(words[2]); - instructions.push_back(new Mul(dst, src)); + instructions.push_back(new Jmp(std::stoi(words[1]))); } catch (const std::invalid_argument& e) { - instructions.push_back(new Mul(dst, std::stoi(words[2]))); + std::cerr << "Invalid immediate: " << words[1] << std::endl; } } - else if (op == "Div") { - Reg dst = strtoreg(words[1]); + else if (op == "Jmpz") { try { - Reg src = strtoreg(words[2]); - instructions.push_back(new Div(dst, src)); + instructions.push_back(new Jmpz(std::stoi(words[1]))); } catch (const std::invalid_argument& e) { - instructions.push_back(new Div(dst, std::stoi(words[2]))); + std::cerr << "Invalid immediate: " << words[1] << std::endl; } } - else if (op == "Load") { - instructions.push_back(new Load(strtoreg(words[1]), std::stoi(words[2]))); - } - else if (op == "Store") { - instructions.push_back(new Store(std::stoi(words[2]), strtoreg(words[1]))); - } - else if (op == "Jmp") { - instructions.push_back(new Jmp(std::stoi(words[1]))); - } - else if (op == "Jmpz") { - instructions.push_back(new Jmpz(std::stoi(words[1]))); - } } return instructions; } @@ -122,7 +104,7 @@ namespace Emulator { while (state._pc < program.size()) { size_t current_pc = state._pc; - program[state._pc]->eval(state); + program[current_pc]->eval(state); if (state._pc == current_pc) { state._pc++; }