summaryrefslogtreecommitdiffstats
path: root/main.hpp
blob: e099986937f914bc0fb8874866e3b84f21e31d2e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#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
		BUFIN,		// input to program in VM is always possible and no blocks
		BUFINCLR,	// clears whatever is in the input buffer
		BUFOUTCLR,	// clears whatever is in the output buffer
		OPTSLEN
	}; // read those last bits with the COPY instruction with source/dest on last pointer
	enum iobits {
		INA,	// input available, clear when read
		IN,	// input bit
		OUTA,	// output available, set when have output, cleared by VM when wrote
		OUT,	// output bit
		IOLEN
	};
	enum instrs {
		COPY,
		NAND
	};
	struct instr {			// deserializirana inštrukcija
		unsigned int s = 0;	// source
		unsigned int d = 0;	// destination
		bool i = 0;		// instruction
		bool p = 0;		// enobitni padding, lahko za metainštrukcije, pri COPY je že
	};	// privzeto inicializiran na NOOP inštrukcijo
	struct def {
		vector<string> args;
		string body;
	};
	class Ov;									
	template<typename value_type, typename index_type> class Ram {
		private:
		public:
			vector<value_type> storage;
			Ov * ov;
			value_type peek (index_type);
			void poke(index_type, value_type);
			Ram (Ov *);
	};
	/* v Program (memory) bi lahko uporabili metainštrukcije (tisti padding bit) v
	 * vsaki inštrukciji in v metainštrukcijah reprezentirali assembly org (lokacijo).
	 * s tem bi lahko imeli npr. 128 biten program counter in s tem zelo preproste jumpe,
	 * ne bi pa bilo treba narediti 2^128 vektorja in posledično binarne datoteke.
	 * Tak način bi bilo verjetno težko implementirati na dejanski strojni opremi,
	 * tukaj pa bi v enem passu čez cel deserializan program memory zaznali te org
	 * metainštrukcije in naredili neko tabelo oziroma prevajalnik program counterja
	 * v lokacijo v vektorju. problem je, da bi se morala prevajalska tabela vsakič znova
	 * regenerirati, ko spreminjamo program memory.
	 * */
	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;
			}
			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;
			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> 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++) {
					io[i] = 0;
				}
			}
			void step (void);
			bool out (void);
			char outc (void);
			void in (bool);
			void inc (char);
			struct instr deserialize (const char [sizeof(struct instr)]);
			void deserialize (istream &, 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);
}