#include #include "main.hpp" using namespace std; using namespace ov; using namespace mup; namespace ov { template value_type Ram::peek (index_type addr) { if (addr < ov->pas) 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 void Ram::poke (index_type addr, value_type val) { if (addr < ov->pas) { ov->pc &= ~(1 << (ov->pas-1-addr)); if (val) 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) 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 Ram::Ram (Ov * ov) { this->ov = ov; storage.resize(ov->rs()+ov->pas, false); for (unsigned int i = 0; i > ov->rs()+ov->pas; i++) { storage[i] = false; } } 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 struct instr š = p.peek(pc++); if (pc >= ps()) pc = 0; switch (š.i) { case COPY: if (š.p) // reading from progmem for (int i = 0; i < pas; i++) 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] = r.peek(š.s+i); for (int i = 0; i < pas; i++) r.poke(š.d+i, b[i]); break; case NAND: r.poke(š.d, !(r.peek(š.s) & r.peek(š.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 = '\0'; for (int i = 0; i < 8; i++) { // bitwise endianness is big bool b = outbuf.front(); 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 (unsigned int i = 0; i < ras(); i++) { if (c[i/8] & 1 << (7-i%8)) r.s |= 1 << i; } 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; 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 (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 (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); 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 string Ov::serialize (struct instr š) { return serialize(&š, 1); } void Ov::deserialize (istream & v = cin, unsigned int o = 0) { string c((istreambuf_iterator(v)), istreambuf_iterator()); // 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) p.poke(o+i/s, deserialize(c.c_str()+i)); } 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++) { if (i % 8 == 0) o << " "; o << (r.peek(i) ? "1" : "0"); } o << endl; } vector assembler (string v) { map defs; vector r; // unsigned int hiaddr = 0; // max address that was written to in r // unsigned int o = 0; // output origin unsigned int i = 0; // input string offset ParserX p(pckCOMMON | pckUNIT | pckCOMPLEX | pckNON_COMPLEX | pckSTRING | pckMATRIX); p.EnableAutoCreateVar(true); while (i < v.length()) { if (i && v[i-1] == '\n' && !v.find("%define ", i)) { i += strlen("%define "); unsigned int ž = v.find('(', i); string dn(""); if (ž == string::npos) { if ((ž = v.find(' ', i)) == string::npos) if ((ž = v.find('\n', i)) == string::npos) throw EndlessArgument; dn = v.substr(i, ž); } else { dn = v.substr(i, v.find('(', ž)); unsigned int k = v.find(')', ž); if (k == string::npos) throw EndlessArgument; string args = v.substr(ž+1, k); while ((k = v.find(',', ž)) != string::npos) { defs[dn].args.push_back(v.substr(ž, k)); ž = k+1; } i = v.find(')', ž); defs[dn].args.push_back(v.substr(ž, i)); } i++; while (v[v.find('\n', i)-1] == '\\') { defs[dn].body.append(v.substr(i, v.find('\n', i))); i = v.find('\n', i)+1; } defs[dn].body.append(v.substr(i, v.find('\n', i))); i = v.find('\n', i)+1; } if (i && v[i-1] == '\n' && !v.find("%macro ", i)) { i += strlen("%macro "); unsigned int ž = v.find(' ', i); if (ž == string::npos) throw EndlessArgument; string dn = v.substr(i, ž++); unsigned int n = atoi(v.substr(++i).c_str()); for (unsigned int j = 0; j < n; j++) { char buffer[69]; sprintf(buffer, "%%%u", j+1); defs[dn].args.push_back(buffer); } if ((i = v.find('\n', i)) == string::npos) throw EndlessBlock; i++; while (v.find("%endmacro", i)) { n = v.find('\n', i)+1; if (n == string::npos) throw EndlessBlock; defs[dn].body.append(v.substr(i, n)); } } if (i && v[i-1] == '\n' && !v.find(".org ", i)) { } } return r; } } int main (int argc, char ** argv) { clog << "OV stands for OB (One Bit) VM (Virtual Machine)." << endl; Ov ov; 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(); ov.step(); } return 0; }