diff options
| Commit | 9a7a8a31e75d1b85274ef46ca29714270062809e (patch) | |
|---|---|---|
| Author | 2018-11-14 19:47:37 +0200 | |
| Committer | 2018-11-14 19:47:37 +0200 | |
| Files | 587a120e78e7c0d50c381d8f38bf0665ed8733a7 /spasm/src/spasm.cpp | |
| IPL-9a7a8a31e75d1b85274ef46ca29714270062809e.tar IPL-9a7a8a31e75d1b85274ef46ca29714270062809e.tar.gz IPL-9a7a8a31e75d1b85274ef46ca29714270062809e.zip | ||
| Parent | 990a8f5e2c2d6e246acfd8b7a99ac1cbf4fb1675 (diff) | |
| Parent | e7c2a14f3e424f1ef1109d7634ece820a058e862 (diff) | |
Merge pull request #45 from SofiaCPP/feature/spasm_improvements
Feature/spasm improvements
1 files changed, 249 insertions, 178 deletions
| -rw-r--r-- | spasm/src/spasm.cpp | 427 |
diff --git a/spasm/src/spasm.cpp b/spasm/src/spasm.cpp index e67c975..d06552d 100644 --- a/spasm/src/spasm.cpp +++ b/spasm/src/spasm.cpp @@ -1,37 +1,12 @@ #include <algorithm> +#include <cassert> #include <iostream> #include "spasm.hpp" namespace SpasmImpl { -PC_t Spasm::op_size = 21; -// Be sure the same number is on the previous and next lines -Operation Spasm::operations[] = {NULL, - &Spasm::push, - &Spasm::pop, - &Spasm::dup, - &Spasm::print, - &Spasm::read, - &Spasm::plus, - &Spasm::minus, - &Spasm::multiply, - &Spasm::divide, - &Spasm::modulus, - &Spasm::gotrue, - &Spasm::gofalse, - &Spasm::go, - &Spasm::call, - &Spasm::ret, - &Spasm::load, - &Spasm::store, - &Spasm::less, - &Spasm::lesseq}; - -Spasm::Spasm() - : pc(0), bc_size(0), bytecode(NULL), istr(&std::cin), ostr(&std::cout) -{ -} +Spasm::Spasm() {} /*! ** Constructs new Spasm object @@ -41,106 +16,187 @@ Spasm::Spasm() ** \param _istr - input stream for the machine ** \param _ostr - output stream for the machine */ -Spasm::Spasm(PC_t _bc_size, - const byte* _bytecode, - std::istream& _istr, - std::ostream& _ostr) - : pc(0), bc_size(_bc_size), istr(&_istr), ostr(&_ostr) -{ - bytecode = new byte[bc_size]; - std::copy(_bytecode, _bytecode + bc_size, bytecode); -} - -Spasm::Spasm(const Spasm& m) - : pc(m.pc), - bc_size(m.bc_size), - data_stack(m.data_stack), - return_stack(m.return_stack), - frame(m.frame), - istr(m.istr), - ostr(m.ostr) -{ - copybc(m); -} +void Spasm::Initialize(PC_t _bc_size, + const byte* _bytecode, + std::istream& _istr, + std::ostream& _ostr) -Spasm::~Spasm() { - deleteobj(); + m_PC = 0; + m_ByteCode.assign(_bytecode, _bytecode + _bc_size); + istr = &_istr; + ostr = &_ostr; + data_stack.resize(1024); + m_SP = &data_stack[0]; + m_FP = &data_stack[0]; } -Spasm& Spasm::operator=(const Spasm& m) -{ - if (this != &m) - { - deleteobj(); - data_stack = m.data_stack; - return_stack = m.return_stack; - frame = m.frame; - istr = m.istr; - ostr = m.ostr; - copybc(m); - } - return *this; -} - -/*! -** Copies the bytecode of another machine m -** -** \param m - the machine from the bytecode is copied -*/ - -void Spasm::copybc(const Spasm& m) -{ - bytecode = new byte[bc_size]; - std::copy(m.bytecode, m.bytecode + bc_size, bytecode); -} - -/*! -** Deletes the machine -*/ -void Spasm::deleteobj() -{ - if (bytecode) - delete[] bytecode; -} - -/*! -** Returns the dstack, so that it can be examined after execution -** -** \return const reference to the data stack -*/ -const Dstack& Spasm::get_dstack() const -{ - return data_stack; -} +Spasm::~Spasm() {} /*! -** Starts the machine. The machine stops if it reaches an invalid opcode +** Runs the machine. The machine stops if it reaches an invalid opcode ** or opcode 0 or the pc reaches beyond the end of the bytecode. +** @return error code for success or failure */ -void Spasm::run() +Spasm::RunResult Spasm::run() { - while (pc < bc_size) + const auto codeSize = m_ByteCode.size(); + while (m_PC < codeSize) { - if (bytecode[pc] > 0 && bytecode[pc] < op_size) - (this->*operations[bytecode[pc]])(); - else + const auto instruction = m_ByteCode[m_PC++]; + const auto opcode = OpCodes(instruction & 0x3f); + const auto size = instruction >> 6; + assert(size == 0 && "no long args yet"); + switch (opcode) { - std::cerr << "opcode got to " << int(bytecode[pc]) << std::endl; - break; + case OpCodes::Halt: + return RunResult::Success; + case OpCodes::Dup: + dup(); + break; + case OpCodes::Pop: + pop(); + break; + case OpCodes::PopTo: + break; + case OpCodes::Push: + { + const auto arg0 = read_reg(size); + push(arg0); + break; + } + case OpCodes::Alloc: + { + const auto count = PC_t(read_reg(size)); + for (PC_t i = 0; i < count; ++i) + { + push_data(0); + } + break; + } + case OpCodes::Print: + { + const auto arg0 = read_reg(size); + print(arg0); + break; + } + case OpCodes::Read: + { + const auto arg0 = read_reg(size); + read(arg0); + break; + } + case OpCodes::Call: + { + const auto arg0 = read_reg(size); + call(arg0); + break; + } + case OpCodes::Ret: + { + const auto arg0 = read_reg(size); + ret(arg0); + break; + } + case OpCodes::Jump: + { + const auto arg0 = read_reg(size); + go(arg0); + break; + } + case OpCodes::JumpT: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + gotrue(arg0, arg1); + break; + } + case OpCodes::JumpF: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + gofalse(arg0, arg1); + break; + } + case OpCodes::Const: + { + const auto reg = read_reg(size); + const auto value = read_number(size); + set_local(reg, value); + break; + } + case OpCodes::Add: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + const auto arg2 = read_reg(size); + plus(arg0, arg1, arg2); + break; + } + case OpCodes::Sub: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + const auto arg2 = read_reg(size); + minus(arg0, arg1, arg2); + break; + } + case OpCodes::Mul: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + const auto arg2 = read_reg(size); + multiply(arg0, arg1, arg2); + break; + } + case OpCodes::Div: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + const auto arg2 = read_reg(size); + divide(arg0, arg1, arg2); + break; + } + case OpCodes::Mod: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + const auto arg2 = read_reg(size); + modulus(arg0, arg1, arg2); + break; + } + case OpCodes::Less: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + const auto arg2 = read_reg(size); + less(arg0, arg1, arg2); + break; + } + case OpCodes::LessEq: + { + const auto arg0 = read_reg(size); + const auto arg1 = read_reg(size); + const auto arg2 = read_reg(size); + lesseq(arg0, arg1, arg2); + break; + } + default: + { + std::cerr << opcode << ": not implemented" << std::endl; + return RunResult::NotImplemented; + } } - ++pc; } + return RunResult::Success; } /*! ** Pushes the next data_t object on the data stack */ -void Spasm::push() +void Spasm::push(reg_t reg) { - ++pc; - data_stack.push(*reinterpret_cast<data_t*>(bytecode + pc)); - pc += sizeof(data_t) - 1; + push_data(get_local(reg)); } /*! @@ -148,7 +204,7 @@ void Spasm::push() */ void Spasm::pop() { - data_stack.pop(); + --m_SP; } /*! @@ -156,82 +212,72 @@ void Spasm::pop() */ void Spasm::dup() { - data_stack.push(data_stack.top()); + push_data(*(m_SP - 1)); } /*! ** Reads a data_t object from the input stream and pushes it on the data ** stack. */ -void Spasm::read() +void Spasm::read(reg_t reg) { data_t x; *istr >> x; - data_stack.push(x); + set_local(reg, x); } /*! ** Pops a data_t object from the data stack and prints it on the output ** stream. */ -void Spasm::print() +void Spasm::print(reg_t reg) { - *ostr << data_stack.pop(); + *ostr << get_local(reg); } /*! ** Pops two data objects from the data stack and pushes their sum on the ** data stack. */ -void Spasm::plus() +void Spasm::plus(reg_t a0, reg_t a1, reg_t a2) { - data_t x, y = data_stack.pop(); - x = data_stack.pop(); - data_stack.push(x + y); + set_local(a0, get_local(a1) + get_local(a2)); } /*! ** Pops two data objects from the data stack and pushes their difference ** on the data stack. */ -void Spasm::minus() +void Spasm::minus(reg_t a0, reg_t a1, reg_t a2) { - data_t x, y = data_stack.pop(); - x = data_stack.pop(); - data_stack.push(x - y); + set_local(a0, get_local(a1) - get_local(a2)); } /*! ** Pops two data objects from the data stack and pushes their product ** on the data stack. */ -void Spasm::multiply() +void Spasm::multiply(reg_t a0, reg_t a1, reg_t a2) { - data_t x, y = data_stack.pop(); - x = data_stack.pop(); - data_stack.push(x * y); + set_local(a0, get_local(a1) * get_local(a2)); } /*! ** Pops two data objects from the data stack and pushes their division ** on the data stack. */ -void Spasm::divide() +void Spasm::divide(reg_t a0, reg_t a1, reg_t a2) { - data_t x, y = data_stack.pop(); - x = data_stack.pop(); - data_stack.push(x / y); + set_local(a0, get_local(a1) / get_local(a2)); } /*! ** Pops two data objects from the data stack and pushes their modulus ** on the data stack. */ -void Spasm::modulus() +void Spasm::modulus(reg_t a0, reg_t a1, reg_t a2) { - data_t x, y = data_stack.pop(); - x = data_stack.pop(); - data_stack.push(x % y); + set_local(a0, get_local(a1) % get_local(a2)); } /*! @@ -239,13 +285,12 @@ void Spasm::modulus() ** If the data_t object on top of the data stack evaluates to true ** execution continues from the place pointed by number in the next bytes */ -void Spasm::gotrue() +void Spasm::gotrue(reg_t a0, reg_t a1) { - ++pc; - if (data_stack.pop()) - pc = *(reinterpret_cast<PC_t*>(bytecode + pc)) - 1; - else - pc += sizeof(size_t) - 1; + if (get_local(a0)) + { + m_PC = a1; + } } /*! @@ -253,48 +298,48 @@ void Spasm::gotrue() ** If the data_t object on top of the data stack evaluates to false ** execution continues from the place pointed by number in the next bytes */ -void Spasm::gofalse() +void Spasm::gofalse(reg_t a0, reg_t a1) { - ++pc; - if (!data_stack.pop()) - pc = *(reinterpret_cast<PC_t*>(bytecode + pc)) - 1; - else - pc += sizeof(size_t) - 1; + if (!get_local(a0)) + { + m_PC = a1; + } } /*! ** Unconditional execution. ** Execution continues from the opcode pointed by the next bytes */ -void Spasm::go() +void Spasm::go(reg_t a0) { - ++pc; - pc = *(reinterpret_cast<PC_t*>(bytecode + pc)) - 1; + m_PC = a0; } /*! ** Function call. A new frame with the specified size is created, the ** return address is saved in the return stack and the new pc is loaded. */ -void Spasm::call() +void Spasm::call(reg_t a0) { - size_t fs; // new frame size - return_stack.push(pc); // using the ++pc in run() - data_stack.pop(&fs, sizeof(fs)); - data_stack.pop(&pc, sizeof(pc)); - frame.new_frame(fs); - --pc; // the ++pc in run() + Frame call{m_PC, PC_t(m_FP - &data_stack[0]), + m_SP - &data_stack[0] - PC_t(*(m_SP - 1)) - 1}; + m_Frames.push(std::move(call)); + m_FP = m_SP - 1; + go(a0); } /*! ** Function return. The frame of the current function is destroyed and the ** saved return address is loaded in the pc */ -void Spasm::ret() +void Spasm::ret(reg_t reg) { - pc = return_stack.top(); - return_stack.pop(); - frame.pop_frame(); + Frame parent = m_Frames.top(); + m_Frames.pop(); + m_SP = &data_stack[parent.StackPointer]; + *(m_SP - 1) = m_FP[reg]; + m_FP = &data_stack[parent.FramePointer]; + m_PC = parent.ReturnAddress; } /*! @@ -303,10 +348,7 @@ void Spasm::ret() */ void Spasm::load() { - size_t offset; - - data_stack.pop(&offset, sizeof(offset)); - data_stack.push(frame[offset]); + assert(false && "not-implemented"); } /*! @@ -314,35 +356,64 @@ void Spasm::load() */ void Spasm::store() { - data_t value = data_stack.pop(); - size_t offset; - - data_stack.pop(&offset, sizeof(offset)); - frame[offset] = value; + assert(false && "not-implemented"); } /*! ** Compares the values on top of the stack. Pushes 1 if the value before ** the top of the stack is less than that on top of the stack. */ -void Spasm::less() +void Spasm::less(reg_t a0, reg_t a1, reg_t a2) { - data_t y = data_stack.pop(); - data_t x = data_stack.pop(); - - data_stack.push(x < y); + set_local(a0, get_local(a1) < get_local(a2)); } /*! ** Compares the values on top of the stack. Pushes 1 if the value before ** the top of the stack is less than or equal to that on top of the stack. */ -void Spasm::lesseq() +void Spasm::lesseq(reg_t a0, reg_t a1, reg_t a2) { - data_t y = data_stack.pop(); - data_t x = data_stack.pop(); + set_local(a0, get_local(a1) <= get_local(a2)); +} + +data_t Spasm::get_local(reg_t reg) +{ + assert(&data_stack[0] <= (m_FP + reg)); + assert((m_FP + reg) < &data_stack[data_stack.size() - 1]); + return m_FP[reg]; +} - data_stack.push(x <= y); +void Spasm::set_local(reg_t reg, data_t data) +{ + assert(&data_stack[0] <= (m_FP + reg)); + assert((m_FP + reg) < &data_stack[data_stack.size() - 1]); + m_FP[reg] = data; +} + +data_t Spasm::pop_data() +{ + auto result = data_stack.back(); + data_stack.pop_back(); + return result; +} + +void Spasm::push_data(data_t data) +{ + assert(m_SP < &data_stack[data_stack.size() - 1]); + *(m_SP++) = data; +} + +reg_t Spasm::read_reg(size_t size) +{ + assert(size == 0); + return m_ByteCode[m_PC++]; +} + +data_t Spasm::read_number(size_t size) +{ + assert(size == 0); + return m_ByteCode[m_PC++]; } } // namespace SpasmImpl |
