#include /* src, dest, instr, padding */ #include "main.hpp" using namespace std; using namespace ov; namespace ov { template value_type Ram::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 void Ram::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 >= (unsigned int) 1 << ov->ras) ov->opts[addr-ov->rs] = val; 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[addr] = val; } template Ram::Ram (Ov * ov) { this->ov = ov; storage.reserve(ov->rs+ov->pas); for (unsigned int i = 0; i <= ov->rs+ov->pas; i++) poke(i, 0); } template // inventor did not say about Program::Program (Ov * ov) { // initializing program memory, so I this->ov = ov; // do not do that. but by "spec", ram storage.reserve(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: 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 i 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; } void Ov::deserialize (istream & v = cin, unsigned int o = 0) { string c((istreambuf_iterator(v)), istreambuf_iterator()); // eof unsigned int s = c.size(); if (s % 2) throw NotAligned; for (unsigned int i = 0; i < s; i += is) { pm[o+i/s] = deserialize(c.c_str()+i); } } } 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(); return 0; }