#include <iostream>
#include "main.hpp"
using namespace std;
using namespace ov;
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 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);
if (val)
ov->pc |= 1 << addr;
} /* we fall through */
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)
while (!ov->outbuf.empty())
ov->outbuf.pop();
}
if (addr >= ov->pas && addr-ov->pas < IOLEN) {
if (ov->opts[BUFIN] && addr-ov->pas == INA && val == 0) { /* machine read */
if (!ov->inbuf.empty()) { /* and wants more. oh, it looks like we */
ov->io[IN] = ov->inbuf.front(); /* have more. we pop the */
ov->inbuf.pop(); /* queue, set the input available bit and */
ov->io[INA] = 1; /* return, so actual set will not be */
return; /* applied, because input is ready. */
} /* if we have no input, we just store the zero */
} /* we do similar things for output. whenever the machine is ready to */
if (ov->opts[BUFOUT] && addr-ov->pas == OUTA && val == 1) { /* output, it */
ov->outbuf.push(ov->io[OUT]); /* sets OUTA bit. we push the OUT */
ov->io[OUT] = 0; /* value into the output queue and clear OUTA bit */
return; /* and therefore indicating the host has read the output. */
}
ov->io[addr-ov->pas] = val;
} /* we fall through, but only if !BUF__ */
storage.at(addr) = val;
}
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.";
}
} 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 (;
case COPY:
/* if (pm(pc++).p) // reading from progmem
for (int i = 0; i < pas; i++)
b[i] = [pm[pc].serialized];
else */
for (int i = 0; i < pas; i++)
b[i] = ram[pm(pc).s+i];
for (int i = 0; i < pas; i++)
ram[pm(pc).d+i] = b[i];
break;
case NAND:
ram[pm(pc).d] = !(ram[pm(pc).s] & ram[pm(pc).d]);
break;
}
}
bool Ov::out () { /* output from machine. throw NotAvailable when there's nothing */
if (opts[BUFOUT]) {
if (outbuf.empty())
throw NotAvailable;
bool o = outbuf.front();
outbuf.pop(); /* unconveniently, pop returns nothing */
return o;
}
if (!(io[OUTA]))
throw NotAvailable;
io[OUTA] = 0;
return io[OUT];
} /* if BUFOUT is set in opts, this is read from the buffer instead */
char Ov::outc () { /* output a byte from machine or throw NotAvailable if not enough bits */
if (!opts[BUFOUT])
throw BufferingRequired;
if (outbuf.size() < 8)
throw NotAvailable;
char r;
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();
}
return r;
} /* buffering must be enabled for this to work. */
void Ov::in (bool i) { /* input to machine. thrw NotAvailable when program hasn't read */
if (opts[BUFOUT]) { /* for BUFOUT input we ONLY insert into the queue if machine */
if (io[INA]) /* "EWOULDBLOCK", in case it can read we put directly to io. */
inbuf.push(i);
io[IN] = i;
io[INA] = 1;
return;
}
if (io[INA]) /* bit is set, so program in VM must first clear the bit. */
throw NotAvailable;
io[IN] = i;
io[INA] = 1;
} /* if BUFIN is set in opts, this is put to the buffer instead */
void Ov::inc (char i) { // input a byte to the machine
if (!opts[BUFIN])
throw BufferingRequired;
if (inbuf.size() < 8)
throw NotAvailable;
for (int x = 7; x >= 0; x--)
in(i & 1 << x);
} // 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))
r.s |= 1 << i;
for (int i = 0; i < ras; i++) {
int j = i+ras;
if (c[j/8] & 1 << (j%8-8))
r.d |= 1 << i;
}
r.i = c[is-1] & 1 << 6;
r.p = c[is-1] & 1 << 7;
return r;
}
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 (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);
}
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);
if (š[i].p)
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
void Ov::deserialize (istream & v = cin, unsigned int o = 0) {
string c((istreambuf_iterator<char>(v)), istreambuf_iterator<char>()); // eof
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);
}
void Ov::pd (ostream & o) {
o << "pc: " << pc << "\t" << "opts:" << (opts[BUFOUT] ? " BUFOUT" : "")
<< (opts[BUFIN] ? " BUFIN" : "") << endl;
o << "ram:";
for (unsigned int i = 0; i < rs; i++) {
if (i % 8 == 0)
o << " ";
o << "" << ram[i];
#pragma message typeof(ram[i])
}
o << endl;
}
}
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;
Ov ov;
ov.deserialize();
while (1) {
ov.pd(cout);
ov.step();
}
return 0;
}