summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile15
-rw-r--r--binbin0 -> 2 bytes
l---------debian/README.Debian1
-rw-r--r--debian/changelog5
-rw-r--r--debian/compat1
-rw-r--r--debian/control13
-rw-r--r--debian/copyright7
-rwxr-xr-xdebian/rules3
-rw-r--r--main.cpp207
-rw-r--r--main.hpp152
11 files changed, 255 insertions, 153 deletions
diff --git a/.gitignore b/.gitignore
index fa90046..396601d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
.gdb_history
ov
a.out
-core
+valgrind-out.txt*
+*core*
diff --git a/Makefile b/Makefile
index 1d860b2..437de56 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,8 @@
DESTDIR=/
SRCFILE=main.cpp
+BINFILE=ov
O=0
-CFLAGS += -Wextra -Wall -g -O$O -I. -oov -finput-charset=UTF-8 -fextended-identifiers
+CFLAGS += -Wextra -Wall -g -O$O -I. -o$(BINFILE) -finput-charset=UTF-8 -fextended-identifiers
LIBS +=
CC=c++
VGARGS += --leak-check=full --track-origins=yes --verbose --log-file=valgrind-out.txt --suppressions=/usr/lib/i386-linux-gnu/valgrind/default.supp --show-leak-kinds=all --leak-resolution=high
@@ -18,19 +19,19 @@ cc:
install:
mkdir -p $(DESTDIR)/usr/bin/
- cp ov $(DESTDIR)/usr/bin/
+ cp $(BINFILE) $(DESTDIR)/usr/bin/
distclean:
- rm ov -f
+ rm $(BINFILE) -f
clean:
- rm ov -f
+ rm $(BINFILE) -f
prepare:
- apt install build-essential -y
+ apt install build-essential libmuparserx-dev -y
-# developing is to be done on i386. for example the default suppression file is hardcoded for i386 here:
+# developing is to be done on i386.
# developing is to be done on bullseye.
valgrind:
- G_SLICE=always-malloc G_DEBUG=gc-friendly valgrind $(VGARGS) ./ov
+ G_SLICE=always-malloc G_DEBUG=gc-friendly valgrind $(VGARGS) ./$(BINFILE)
diff --git a/bin b/bin
new file mode 100644
index 0000000..ac7ac5f
--- /dev/null
+++ b/bin
Binary files differ
diff --git a/debian/README.Debian b/debian/README.Debian
new file mode 120000
index 0000000..59a23c4
--- /dev/null
+++ b/debian/README.Debian
@@ -0,0 +1 @@
+../README \ No newline at end of file
diff --git a/debian/changelog b/debian/changelog
new file mode 100644
index 0000000..ad34558
--- /dev/null
+++ b/debian/changelog
@@ -0,0 +1,5 @@
+ov (0.0.0-1) stable; urgency=low
+
+ * Initial release.
+
+ -- Anton Luka Šijanec <anton@sijanec.eu> Wed, 26 Jan 2022 14:30:00 +0200
diff --git a/debian/compat b/debian/compat
new file mode 100644
index 0000000..b4de394
--- /dev/null
+++ b/debian/compat
@@ -0,0 +1 @@
+11
diff --git a/debian/control b/debian/control
new file mode 100644
index 0000000..a7c41c8
--- /dev/null
+++ b/debian/control
@@ -0,0 +1,13 @@
+Source: ov
+Section: otherosfs
+Priority: optional
+Maintainer: Anton Luka Šijanec <anton@sijanec.eu>
+Build-Depends: debhelper (>=11~), libmuparserx-dev
+Standards-Version: 4.1.4
+Homepage: http://git.sijanec.eu/sijanec/ov
+
+Package: ov
+Architecture: any
+Multi-Arch: foreign
+Depends: ${misc:Depends}, ${shlibs:Depends}
+Description: ov stands for ob (one bit) vm (virtual machine)
diff --git a/debian/copyright b/debian/copyright
new file mode 100644
index 0000000..5a0b54f
--- /dev/null
+++ b/debian/copyright
@@ -0,0 +1,7 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: sear.c
+Upstream-Contact: Anton Luka Šijanec <anton@sijanec.eu>
+Source: http://git.sijanec.eu/sijanec/sear.c
+Copyright: 2021 Anton Luka Šijanec
+Disclaimer: This package is not licensed under any license.
+Comment: At least in current version of this program, no licensing information was published.
diff --git a/debian/rules b/debian/rules
new file mode 100755
index 0000000..cbe925d
--- /dev/null
+++ b/debian/rules
@@ -0,0 +1,3 @@
+#!/usr/bin/make -f
+%:
+ dh $@
diff --git a/main.cpp b/main.cpp
index b0c805f..74b9fad 100644
--- a/main.cpp
+++ b/main.cpp
@@ -1,4 +1,3 @@
-#include <iostream>
#include "main.hpp"
using namespace std;
using namespace ov;
@@ -6,26 +5,27 @@ namespace ov {
template<typename value_type, typename index_type>
value_type Ram<value_type, index_type>::peek (index_type addr) {
if (addr < ov->pas)
- return ov->pc & 1 << addr;
- if (addr >= (unsigned int) 1 << ov->ras)
- return ov->opts[addr-(1 << ov->ras)];
- if (addr-ov->pas < IOLEN)
- return ov->io[addr-ov->rs];
+ return ov->pc & 1 << (ov->pas-1-addr);
+ if (addr-ov->pas < IOLEN) {
+ return ov->io[addr-ov->pas];
+ }
+ if (addr >= (unsigned int) 1 << ov->ras())
+ return ov->opts[addr-(1 << ov->ras())];
return storage[addr];
}
template<typename value_type, typename index_type>
void Ram<value_type, index_type>::poke (index_type addr, value_type val) {
if (addr < ov->pas) {
- ov->pc &= ~(1 << addr);
+ ov->pc &= ~(1 << (ov->pas-1-addr));
if (val)
- ov->pc |= 1 << addr;
+ ov->pc |= 1 << (ov->pas-1-addr);
} /* we fall through */
- if (addr >= ov->rs) {
- ov->opts[addr-ov->rs] = val;
- if (val && addr-ov->rs == BUFINCLR)
+ if (addr >= ov->rs()) {
+ ov->opts[addr-ov->rs()] = val;
+ if (val && addr-ov->rs() == BUFINCLR)
while (!ov->inbuf.empty())
ov->inbuf.pop();
- if (val && addr-ov->rs == BUFOUTCLR)
+ if (val && addr-ov->rs() == BUFOUTCLR)
while (!ov->outbuf.empty())
ov->outbuf.pop();
}
@@ -50,48 +50,30 @@ namespace ov {
template<typename value_type, typename index_type>
Ram<value_type, index_type>::Ram (Ov * ov) {
this->ov = ov;
- storage.resize(ov->rs+ov->pas, 0);
- }
- template<typename value_type, typename index_type> // inventor did not say about
- Program<value_type, index_type>::Program (Ov * ov) { // initializing program memory, so I
- this->ov = ov; // do not do that. but by "spec", ram
- storage.resize(ov->ps); // is set to zero and code starts at
- } // pc == 0, unless of course set with
- struct NotImplemented : public exception { // Ov::pc.
- const char * what () const throw () {
- return "Not implemented.";
- }
- } NotImplemented;
- struct NotAvailable : public exception {
- const char * what () const throw () {
- return "I/O is currently not available. You can try again.";
- }
- } NotAvailable;
- struct BufferingRequired : public exception {
- const char * what () const throw () {
- return "I/O buffering must be enabled for this function, but it's not.";
+ storage.resize(ov->rs()+ov->pas, false);
+ for (unsigned int i = 0; i > ov->rs()+ov->pas; i++) {
+ storage[i] = false;
}
- } BufferingRequired;
- struct NotAligned : public exception {
- const char * what () const throw () {
- return "Byte count can't be deserialized without remainder.";
- }
- } NotAligned;
+ }
void Ov::step () { // ko se izvaja inštrukcija, kaže števec programa na naslednjo.
bool b[pas]; // buffer. you have to first read all and then write for COPY
- switch (pm(pc++).i) { // predstavljaj si, da so oklepaji okoli pc++ oglati (;
+ struct instr š = p.peek(pc++);
+ if (pc >= ps())
+ pc = 0;
+ switch (š.i) {
case COPY:
- /* if (pm(pc++).p) // reading from progmem
+ if (š.p) // reading from progmem
for (int i = 0; i < pas; i++)
- b[i] = [pm[pc].serialized];
- else */
+ b[i] = 1 << (7-š.s%pas%8)&serialize(p.peek(š.s/pas))
+ .c_str()[š.s%pas/8];
+ else
for (int i = 0; i < pas; i++)
- b[i] = ram[pm(pc).s+i];
+ b[i] = r.peek(š.s+i);
for (int i = 0; i < pas; i++)
- ram[pm(pc).d+i] = b[i];
+ r.poke(š.d+i, b[i]);
break;
case NAND:
- ram[pm(pc).d] = !(ram[pm(pc).s] & ram[pm(pc).d]);
+ r.poke(š.d, !(r.peek(š.s) & r.peek(š.d)));
break;
}
}
@@ -113,10 +95,9 @@ namespace ov {
throw BufferingRequired;
if (outbuf.size() < 8)
throw NotAvailable;
- char r;
+ char r = '\0';
for (int i = 0; i < 8; i++) { // bitwise endianness is big
bool b = outbuf.front();
- r &= ~(1 << (8-i));
if (b)
r |= 1 << (8-i);
outbuf.pop();
@@ -146,12 +127,13 @@ namespace ov {
} // buffering must be enabled for this to work.
struct instr Ov::deserialize (const char * c) { // treats c as array of is size
struct instr r;
- for (int i = 0; i < ras; i++)
- if (c[i/8] & 1 << (i%8-8))
+ for (unsigned int i = 0; i < ras(); i++) {
+ if (c[i/8] & 1 << (7-i%8))
r.s |= 1 << i;
- for (int i = 0; i < ras; i++) {
- int j = i+ras;
- if (c[j/8] & 1 << (j%8-8))
+ }
+ for (unsigned int i = 0; i < ras(); i++) {
+ unsigned int j = i+ras();
+ if (c[j/8] & 1 << (7-j%8))
r.d |= 1 << i;
}
r.i = c[is-1] & 1 << 6;
@@ -161,54 +143,125 @@ namespace ov {
string Ov::serialize (struct instr * š, unsigned int n) {
char r[sizeof(š)*n];
for (unsigned int i = 0; i < n; i++) {
- for (int j = 0; j < ras; j++) {
- r[i*is+j/8] &= ~(1 << ((ras-1)-j));
- if (š[i].s & 1 << ((ras-1)-j))
- r[i*is+j/8] |= 1 << ((ras-1)-j);
+ for (unsigned int j = 0; j < ras(); j++) {
+ r[i*is+j/8] &= ~(1 << ((ras()-1)-j));
+ if (š[i].s & 1 << ((ras()-1)-j))
+ r[i*is+j/8] |= 1 << ((ras()-1)-j);
}
- for (int j = 0; j < ras; j++) {
- int k = j+ras;
- r[i*is+k/8] &= ~(1 << ((ras-1)-j));
- if (š[i].d & 1 << ((ras-1)-j))
- r[i*is+k/8] |= 1 << ((ras-1)-j);
+ for (unsigned int j = 0; j < ras(); j++) {
+ unsigned int k = j+ras();
+ r[i*is+k/8] &= ~(1 << ((ras()-1)-j));
+ if (š[i].d & 1 << ((ras()-1)-j))
+ r[i*is+k/8] |= 1 << ((ras()-1)-j);
}
- r[i*is+(2*ras)/8] &= 1 << (ras-2);
+ r[i*is+(2*ras())/8] &= 1 << (ras()-2);
if (š[i].i)
- r[i*is+(2*ras)/8] |= 1 << (ras-2);
- r[i*is+(2*ras)/8] &= 1 << (ras-1);
+ r[i*is+(2*ras())/8] |= 1 << (ras()-2);
+ r[i*is+(2*ras())/8] &= 1 << (ras()-1);
if (š[i].p)
- r[i*is+(2*ras)/8] |= 1 << (ras-1);
+ r[i*is+(2*ras())/8] |= 1 << (ras()-1);
}
return string(r); // ugly hack, C/C++ in 2022 still can't return arrays from function
} // serialize(&pstor.storage[0], pstor.storage.size) is valid, because pm has no special MMU
+ string Ov::serialize (struct instr š) {
+ return serialize(&š, 1);
+ }
void Ov::deserialize (istream & v = cin, unsigned int o = 0) {
string c((istreambuf_iterator<char>(v)), istreambuf_iterator<char>()); // eof
+ deserialize(c, o);
+ }
+ void Ov::deserialize (string c, unsigned int o = 0) {
unsigned int s = c.size();
if ((o+s) % is)
throw NotAligned;
for (unsigned int i = 0; i < s; i += is)
- pm[o+i/s] = deserialize(c.c_str()+i);
+ p.poke(o+i/s, deserialize(c.c_str()+i));
}
- void Ov::pd (ostream & o) {
- o << "pc: " << pc << "\t" << "opts:" << (opts[BUFOUT] ? " BUFOUT" : "")
- << (opts[BUFIN] ? " BUFIN" : "") << endl;
+ void Ov::pd (ostream & o = clog) {
+ o << "pc: " << pc << "\topts:" << (opts[BUFOUT] ? " BUFOUT" : "")
+ << (opts[BUFIN] ? " BUFIN" : "") << "\tinstruction: " <<
+ (p.peek(pc).i == COPY ? "COPY from " : "NAND from ") << p.peek(pc).s <<
+ "\tto " << p.peek(pc).d << "\twith meta bit " << p.peek(pc).p << endl;
o << "ram:";
- for (unsigned int i = 0; i < rs; i++) {
+ for (unsigned int i = 0; i < rs(); i++) {
if (i % 8 == 0)
o << " ";
- o << "" << ram[i];
-#pragma message typeof(ram[i])
+ o << (r.peek(i) ? "1" : "0");
}
o << endl;
}
+ vector<struct instr> assembler (string v) {
+ map<string, struct def> defs;
+ vector<struct instr>;
+ int hiaddr = 0;
+ int i = 0;
+ while (i < v.length) {
+ if (i && v[i-1] == '\n' && !v.find("%define ", i)) {
+ i += strlen("%define ");
+ int ž = s.find('(', i);
+ string dn("");
+ if (ž == string::npos) {
+ if ((ž = s.find(' ', i)) == string::npos)
+ if ((ž = s.find('\n', i)) == string::npos)
+ throw EndlessArgument;
+ dn = s.substr(i, ž);
+ } else {
+ dn = s.substr(i, s.find('(', ž));
+ int k = s.find(')', ž);
+ if (k == string::npos)
+ throw EndlessArgument;
+ string args = s.substr(ž+1, k);
+ while ((k = s.find(',', ž)) != string::npos) {
+ defs[dn].args.push_back(s.substr(ž, k));
+ ž = k+1;
+ }
+ i = s.find(')', ž);
+ defs[dn].args.push_back(s.substr(ž, i));
+ }
+ i++;
+ while (s[s.find('\n', i)-1] == '\\') {
+ defs[dn].body.append(s.substr(i, s.find('\n', i)));
+ i = s.find('\n', i)+1;
+ }
+ defs[dn].body.append(s.substr(i, s.find('\n', i)));
+ i = s.find('\n', i)+1;
+ }
+ if (i && v[i-1] == '\n' && !v.find("%macro ", i)) {
+ i += strlen("%macro ");
+ if ((ž = s.find(' ', i)) == string::npos)
+ throw EndlessArgument;
+ string dn = s.substr(i, ž++);
+ n = atoi(s.substring(++i));
+ for (int j = 0; j < n; j++)
+ defs[dn].args.push_back("%" << n+1);
+ if ((i = s.find('\n', i)) == string::npos)
+ throw EndlessBlock;
+ i++;
+ while (s.find("%endmacro", i)) {
+ n = s.find('\n', i)+1;
+ if (n == str::npos)
+ throw EndmacroMissing;
+ defs[dn].body.append(s.substr(i, n));
+ }
+ }
+ }
+ }
}
-int main (void /* int argc, char ** argv */) {
- clog << "OV stands for OB (One Bit) VM (Virtual Machine)." << endl
- << "Stanard input is ready to accept a binary." << endl;
+int main (int argc, char ** argv) {
+ clog << "OV stands for OB (One Bit) VM (Virtual Machine)." << endl;
Ov ov;
- ov.deserialize();
+ ov.pd();
+ if (argc < 2 || !argv[1]) {
+ clog << "Stanard input is ready to accept a binary." << endl
+ << "You can also specify the binary filename in argv[1]" << endl;
+ ov.deserialize();
+ } else {
+ ifstream exe = ifstream(argv[1]);
+ ov.pd();
+ ov.deserialize(exe);
+ }
while (1) {
- ov.pd(cout);
+ ov.pd();
ov.step();
}
return 0;
diff --git a/main.hpp b/main.hpp
index 58a7337..1794b7a 100644
--- a/main.hpp
+++ b/main.hpp
@@ -1,8 +1,52 @@
#include <vector>
#include <queue>
#include <string>
+#include <sstream>
+#include <iostream>
+#include <fstream>
+#include <regex>
namespace ov {
using namespace std;
+ struct NotImplemented : public exception {
+ const char * what () const throw () {
+ return "Not implemented.";
+ }
+ } NotImplemented;
+ struct NotAvailable : public exception {
+ const char * what () const throw () {
+ return "I/O is currently not available. You can try again.";
+ }
+ } NotAvailable;
+ struct BufferingRequired : public exception {
+ const char * what () const throw () {
+ return "I/O buffering must be enabled for this function, but it's not.";
+ }
+ } BufferingRequired;
+ struct NotAligned : public exception {
+ const char * what () const throw () {
+ return "Byte count can't be deserialized without remainder.";
+ }
+ } NotAligned;
+ struct AssemblerFatal : public exception {
+ const char * what () const throw () {
+ return "Fatal error in assembler.";
+ }
+ } AssemblerFatal;
+ struct SyntaxError : AssemblerFatal {
+ const char * what () const throw () {
+ return "Fatal syntax error in assembler.";
+ }
+ } SyntaxError;
+ struct EndlessArgument : public SyntaxError {
+ const char * what () const throw () {
+ return "Some builtin preprocessor command does not have enough arguments.";
+ }
+ } EndlessArgument;
+ struct EndlessBlock : public SyntaxError {
+ const char * what () const throw () {
+ return "Some builtin preprocessor block %* is not terminated with %end*.";
+ }
+ } EndlessBlock;
enum opts { // opts are pas-1 bits aftr last ram ptr (2**ras)-1 and can be w/r from prg
DEFAULT,
BUFOUT, // output from program in VM is always possible and no blocks
@@ -28,34 +72,10 @@ namespace ov {
bool i = 0; // instruction
bool p = 0; // enobitni padding, lahko za metainštrukcije, pri COPY je že
}; // privzeto inicializiran na NOOP inštrukcijo
- template<typename value_type, typename index_type, class Memory> class Mmu {
- private:
- class Cell {
- private:
- friend class Mmu;
- Mmu & mmu;
- index_type index;
- Cell (Mmu & mmu, index_type index)
- : mmu(mmu), index(index) {}
- public:
- operator value_type (void) {
- return mmu.memory.peek(index);
- }
- value_type operator= (value_type value) {
- mmu.memory.poke(index, value);
- return value;
- }
- };
- public:
- Memory & memory;
- Mmu (Memory & memory) : memory(memory) {}
- Cell operator[] (index_type index) {
- return Cell(*this, index);
- }
- value_type & operator() (index_type index) {
- return memory.peek(index);
- }
- };
+ struct def {
+ vector<string> args;
+ string body("");
+ }
class Ov;
template<typename value_type, typename index_type> class Ram {
private:
@@ -79,67 +99,60 @@ namespace ov {
template<typename value_type, typename index_type> class Program {
private:
public:
+ index_type hiaddr;
vector<value_type> storage;
+ const struct instr empty;
Ov * ov;
value_type & peek (index_type addr) {
+ if (addr > hiaddr) {
+ hiaddr = addr;
+ storage.resize(hiaddr+1, empty);
+ }
return storage[addr];
}
void poke (index_type addr, value_type val) {
+ if (addr > hiaddr) {
+ hiaddr = addr;
+ storage.resize(hiaddr+1, empty /* avoid leaking memory */);
+ }
storage[addr] = val;
}
- Program (Ov *);
+ index_type length () {
+ return hiaddr+1;
+ }
+ Program (Ov * ov) : ov(ov) {
+ hiaddr = 0;
+ storage.push_back(empty); // we don't want to leak memory contents
+ }
};
class Ov {
private:
public:
queue<bool> inbuf;
queue<bool> outbuf;
-#define IS2RAS(is) ((is*8-2)/2)
- class ras { // IS
- private: // IT
- public: // REALLY
- unsigned short int & is; // THIS
- operator unsigned short int (void) { // HARD
- return IS2RAS(is); // TO
- } // DEFINE
- ras (class Ov * ov) : is(ov->is) {} // A
- }; // GETTER
- ras ras{this}; // FUNCTION
- class rs {
- private:
- public:
- unsigned short int & is;
- operator unsigned int (void) {
- return 1 << IS2RAS(is);
- }
- rs (class Ov * ov) : is(ov->is) {}
- };
- rs rs{this};
- class ps {
- private:
- public:
- unsigned short int & pas;
- operator unsigned int (void) {
- return 1 << pas;
- }
- ps (class Ov * ov) : pas(ov->pas) {}
- };
- ps ps{this};
+ unsigned int ras (void) {
+ return (is*8-2)/2;
+ }
+ unsigned int rs (void) {
+ return 1 << ras();
+ }
+ unsigned int ps (void) {
+ return 1 << pas;
+ }
unsigned short int is;
unsigned short int pas;
bool io[IOLEN];
bool opts[OPTSLEN];
unsigned int pc = 0; /* where the program starts --- at zero or where? */
- Ram<bool, unsigned int> rstor{this};
- Mmu<bool, unsigned int, class Ram<bool, unsigned int>> ram{rstor};
- Program<struct instr, unsigned int> pstor{this};
- Mmu<struct instr, unsigned, class Program<struct instr, unsigned>> pm{pstor};
- Ov (unsigned short int is = 2, unsigned short int pas = 16)
- : is(is), pas(pas) { // add bound checks
+ Ram<bool, unsigned int> r{this};
+ Program<struct instr, unsigned int> p{this};
+ Ov (unsigned short int is = 2, unsigned short int pas = 16) :
+ is(is), pas(pas) { // TODO: check
for (int i = 0; i < OPTSLEN; i++)
opts[i] = 0;
- for (int i = 0; i < IOLEN; i++)
+ for (int i = 0; i < IOLEN; i++) {
io[i] = 0;
+ }
}
void step (void);
bool out (void);
@@ -148,7 +161,10 @@ namespace ov {
void inc (char);
struct instr deserialize (const char [sizeof(struct instr)]);
void deserialize (istream &, unsigned int);
- string serialize (struct instr * i, unsigned int);
+ void deserialize (string, unsigned int);
+ string serialize (struct instr *, unsigned int);
+ string serialize (struct instr);
void pd (ostream &);
};
+ vector<struct instr> assembler (string);
}