diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Makefile | 15 | ||||
-rw-r--r-- | bin | bin | 0 -> 2 bytes | |||
l--------- | debian/README.Debian | 1 | ||||
-rw-r--r-- | debian/changelog | 5 | ||||
-rw-r--r-- | debian/compat | 1 | ||||
-rw-r--r-- | debian/control | 13 | ||||
-rw-r--r-- | debian/copyright | 7 | ||||
-rwxr-xr-x | debian/rules | 3 | ||||
-rw-r--r-- | main.cpp | 207 | ||||
-rw-r--r-- | main.hpp | 152 |
11 files changed, 255 insertions, 153 deletions
@@ -1,4 +1,6 @@ .gdb_history ov a.out -core +valgrind-out.txt* +*core* +ž @@ -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) Binary files differdiff --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 $@ @@ -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; @@ -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); } |