Website Summary Files Commits Branches @commit @diff @stats
path: root / spasm
diff options
context:
space:
mode:
Commitf81f1c56cc01ec3d91a73ec7314519ac59abd5b4 (patch)
Author
 Dimitar Trendafilov <dimitar@coherent-labs.com>
2018-11-12 23:41:13 +0200
Committer
 Dimitar Trendafilov <dimitar@coherent-labs.com>
2018-11-12 23:41:13 +0200
Files2ee6165cd93f06dbf77c7057d48aad921ea67863 /spasm
IPL-f81f1c56cc01ec3d91a73ec7314519ac59abd5b4.tar
IPL-f81f1c56cc01ec3d91a73ec7314519ac59abd5b4.tar.gz
IPL-f81f1c56cc01ec3d91a73ec7314519ac59abd5b4.zip
Parent679c29818416d98f974f79d108d9a732d635e1a3 (diff)
Converted spasm to a register machine
3 files changed, 251 insertions, 184 deletions
-rw-r--r--spasm/src/spasm.cpp342
-rw-r--r--spasm/src/spasm_impl.hpp91
-rw-r--r--spasm/src/types.hpp2
diff --git a/spasm/src/spasm.cpp b/spasm/src/spasm.cpp
index 857143f..60f7531 100644
--- a/spasm/src/spasm.cpp
+++ b/spasm/src/spasm.cpp
@@ -1,40 +1,12 @@
#include <algorithm>
-#include <iostream>
#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
@@ -48,92 +20,155 @@ 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)
+ : m_PC(0),
+ m_ByteCode(_bytecode, _bytecode + _bc_size),
+ istr(&_istr),
+ ostr(&_ostr)
{
- copybc(m);
+ data_stack.resize(1024);
+ m_SP = &data_stack[0];
+ m_FP = &data_stack[0];
}
-Spasm::~Spasm()
-{
- deleteobj();
-}
-
-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;
-}
+Spasm::~Spasm() {}
/*!
-** 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;
-}
-
-/*!
-** 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 size = m_ByteCode.size();
+ while (m_PC < size)
{
- 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");
+ (void)size;
+ 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::Print:
+ {
+ const auto arg0 = read_reg(size);
+ print(arg0);
+ break;
+ }
+ case OpCodes::Read:
+ {
+ const auto arg0 = read_reg(size);
+ read(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::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;
- push_data(*reinterpret_cast<data_t*>(bytecode + pc));
- pc += sizeof(data_t) - 1;
+ push_data(get_local(reg));
}
/*!
@@ -141,7 +176,7 @@ void Spasm::push()
*/
void Spasm::pop()
{
- data_stack.pop_back();
+ --m_SP;
}
/*!
@@ -149,83 +184,72 @@ void Spasm::pop()
*/
void Spasm::dup()
{
- push_data(data_stack.back());
+ 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;
- push_data(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.back();
- 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 = pop_data();
- x = pop_data();
- push_data(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 = pop_data();
- x = pop_data();
- push_data(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 y = pop_data(),
- x = pop_data();
- push_data(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 y = pop_data(),
- x = pop_data();
- push_data(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 y = pop_data(),
- x = pop_data();
- push_data(x % y);
+ set_local(a0, get_local(a1) % get_local(a2));
}
/*!
@@ -233,13 +257,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 (pop_data())
- pc = *(reinterpret_cast<PC_t*>(bytecode + pc)) - 1;
- else
- pc += sizeof(size_t) - 1;
+ if (get_local(a0))
+ {
+ m_PC = a1;
+ }
}
/*!
@@ -247,23 +270,21 @@ 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 (!pop_data())
- 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;
}
/*!
@@ -287,11 +308,15 @@ void Spasm::call()
** 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)
{
+ // TODO: Implement
+ (void)reg;
+#if 0
pc = return_stack.top();
return_stack.pop();
frame.pop_frame();
+#endif
}
/*!
@@ -315,24 +340,32 @@ void Spasm::store()
** 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 = pop_data();
- data_t x = pop_data();
-
- push_data(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 = pop_data();
- data_t x = pop_data();
+ set_local(a0, get_local(a1) <= get_local(a2));
+}
- push_data(x <= y);
+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];
+}
+
+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()
@@ -344,7 +377,14 @@ data_t Spasm::pop_data()
void Spasm::push_data(data_t data)
{
- data_stack.push_back(data);
+ assert(m_SP < &data_stack[data_stack.size() - 1]);
+ *(m_SP++) = data;
+}
+
+reg_t Spasm::read_reg(int size)
+{
+ assert(size == 0);
+ return m_ByteCode[m_PC++];
}
} // namespace SpasmImpl
diff --git a/spasm/src/spasm_impl.hpp b/spasm/src/spasm_impl.hpp
index 7c4d8f7..487f130 100644
--- a/spasm/src/spasm_impl.hpp
+++ b/spasm/src/spasm_impl.hpp
@@ -8,6 +8,28 @@
namespace SpasmImpl
{
+enum OpCodes : char
+{
+ Halt,
+ Dup,
+ Pop,
+ PopTo,
+ Push,
+ Print,
+ Read,
+ Call,
+ Ret,
+ Jump,
+ JumpT,
+ JumpF,
+ Add,
+ Sub,
+ Mul,
+ Div,
+ Mod,
+ Less,
+ LessEq,
+};
//! The Abstract Stack Machine
/*!
** The machine contains a data stack for operations and control flow and
@@ -22,32 +44,35 @@ class Spasm
const byte*,
std::istream& = std::cin,
std::ostream& = std::cout);
- Spasm(const Spasm&);
~Spasm();
- Spasm& operator=(const Spasm&);
+ Spasm(const Spasm&) = delete;
+ Spasm& operator=(const Spasm&) = delete;
- void run();
+ enum RunResult {
+ Success,
+ Exception,
+ NotImplemented,
+ };
+ RunResult run();
private:
- //! Operations (opcodes) of the machine
- static Operation operations[];
-
- //! number of operations
- static PC_t op_size;
-
//! Program counter - points the current opcode
- PC_t pc;
-
- //! size of the program bytecode
- PC_t bc_size;
+ PC_t m_PC = 0;
+ typedef SPVector<byte> ByteCode;
//! bytecode of the program
- byte* bytecode;
+ ByteCode m_ByteCode;
typedef SPVector<data_t> DataStack;
- //! Data stack for arithmetic operations and control flow
+ //! stack for storing arguments and local variables
DataStack data_stack;
+ //! Stack pointer - always the top of the stack
+ data_t* m_SP = nullptr;
+
+ //! Frame pointer - the start of the stack for the current function
+ data_t* m_FP = nullptr;
+
//! Return stack for the addresses of the returns
Rstack_t return_stack;
@@ -60,38 +85,38 @@ class Spasm
//! Output stream for print () opertion
std::ostream* ostr;
- void push();
+ void push(reg_t reg);
+ void pop(reg_t reg);
void pop();
void dup();
- void print();
- void read();
+ void print(reg_t reg);
+ void read(reg_t reg);
- void plus();
- void minus();
- void multiply();
- void divide();
- void modulus();
+ void plus(reg_t a0, reg_t a1, reg_t a2);
+ void minus(reg_t a0, reg_t a1, reg_t a2);
+ void multiply(reg_t a0, reg_t a1, reg_t a2);
+ void divide(reg_t a0, reg_t a1, reg_t a2);
+ void modulus(reg_t a0, reg_t a1, reg_t a2);
- void gotrue();
- void gofalse();
- void go();
+ void gotrue(reg_t a0, reg_t a1);
+ void gofalse(reg_t a0, reg_t a1);
+ void go(reg_t a0);
void call();
- void ret();
+ void ret(reg_t a0);
void load();
void store();
- void less();
- void lesseq();
-
- // private method for copying and deleting an object
- void copybc(const Spasm&);
- void deleteobj();
+ void less(reg_t a0, reg_t a1, reg_t a2);
+ void lesseq(reg_t a0, reg_t a1, reg_t a2);
+ data_t get_local(reg_t reg);
+ void set_local(reg_t reg, data_t data);
data_t pop_data();
void push_data(data_t);
+ reg_t read_reg(int size);
};
} // namespace SpasmImpl
diff --git a/spasm/src/types.hpp b/spasm/src/types.hpp
index 4a8606b..ec5a0cb 100644
--- a/spasm/src/types.hpp
+++ b/spasm/src/types.hpp
@@ -17,5 +17,7 @@ typedef void (Spasm::*Operation)();
template <typename T>
using SPVector = std::vector<T>;
+
+typedef long long int reg_t;
} // namespace SpasmImpl
#endif // ----- #ifndef TYPES_HPP