#include #include #include #include "isa/isa.h" instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned int bits, bool interrupt) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-variable" bool ied = (flags >> 12) & 1; bool gt = (flags >> 10) & 1; bool ir = (flags >> 9) & 1; bool ii = (flags >> 8) & 1; bool ie = (flags >> 7) & 1; bool u = (flags >> 6) & 1; bool ifb = (flags >> 3) & 7; bool df = flags & 7; #pragma GCC diagnostic pop instruction_context inst; inst.next_pc = (pc & ~07777) | ((pc + 1) & 07777); if (interrupt) { bits = 04000; assert(df == 0); assert(ifb == 0); inst.next_pc = pc; pc = 0; } switch (bits >> 9) { case 0: // AND bitwise and inst.need_exec_load = true; inst.need_read_acc = true; inst.need_write_acc = true; inst.ef = [](auto &ctx) { ctx.acc = ctx.acc.value() & ctx.data.value() & 07777; }; break; case 1: // TAD two's complement addition inst.need_exec_load = true; inst.need_read_acc = true; inst.need_read_link = true; inst.need_write_acc = true; inst.need_write_link = true; inst.ef = [](auto &ctx) { unsigned int sum = ctx.acc.value() + ctx.data.value(); ctx.link = ctx.link.value() ^ (sum >> 12); ctx.acc = sum & 07777; }; break; case 2: // ISZ increment and skip if zero inst.need_exec_load = true; inst.need_exec_store = true; inst.possibly_redirects = true; inst.ef = [](auto &ctx) { ctx.data = (ctx.data.value() + 1) & 07777; if (*ctx.data == 0) ctx.next_pc = (ctx.next_pc & ~07777) | ((ctx.next_pc + 1) & 07777); }; break; case 3: // DCA deposit and clear accumulator inst.need_read_acc = true; inst.need_write_acc = true; inst.need_exec_store = true; inst.ef = [](auto &ctx) { ctx.data = ctx.acc.value(); ctx.acc = 0; }; break; case 4: // JMS jump subroutine inst.need_exec_store = true; inst.possibly_redirects = true; inst.ef = [ifb](auto &ctx) { ctx.data = ctx.next_pc; ctx.next_pc = (ifb << 12) | ((ctx.final_address.value() + 1) & 07777); }; break; case 5: // JMP jump inst.possibly_redirects = true; inst.ef = [ifb](auto &ctx) { ctx.next_pc = (ifb << 12) | (ctx.final_address.value() & 07777); }; break; case 6: // IOT switch ((bits >> 3) & 077) { case 000: // INTERRUPT CONTROLLER switch (bits & 07) { case 0: // SKON skip if interrupts enabled inst.possibly_redirects = true; inst.ef = [ie](auto &ctx) { if (ie) ctx.next_pc = (ctx.next_pc & 07777) | ((ctx.next_pc + 1) & 07777); }; break; case 1: // ION set int_enable_delay inst.write_ctlreg = FLAGS; inst.ef = [flags](auto &ctx) { ctx.ctlval = flags | FLAG_INT_ENABLE_DELAY; }; break; case 2: // IOF clear int_enable and int_enable_delay inst.write_ctlreg = FLAGS; inst.ef = [flags](auto &ctx) { ctx.ctlval = flags & ~FLAG_INT_ENABLE_DELAY & ~FLAG_INT_ENABLE; }; break; case 3: // SRQ skip if pending interrupt inst.possibly_redirects = true; inst.ef = [ir](auto &ctx) { if (ir) ctx.next_pc = (ctx.next_pc & 07777) | ((ctx.next_pc + 1) & 07777); }; break; case 4: // GTF get flags inst.read_ctlreg = FLAGS_SAVED; inst.need_read_link = true; inst.ef = [](auto &ctx) { auto flags = ctx.ctlval.value(); flags |= (unsigned int)ctx.link.value() << 11; ctx.acc = flags; }; break; case 5: // RTF restore flags inst.need_read_acc = true; inst.need_write_link = true; inst.write_ctlreg = FLAGS; inst.ef = [ie](auto &ctx) { if (ie && (ctx.acc.value() & FLAG_INT_ENABLE)) { ctx.ctlval = ctx.acc.value() | FLAG_INT_ENABLE_DELAY; } else if (ctx.acc.value() & FLAG_INT_ENABLE) { ctx.ctlval = (ctx.acc.value() | FLAG_INT_ENABLE_DELAY) & ~FLAG_INT_ENABLE; } else { ctx.ctlval = ctx.acc.value(); } ctx.link = ctx.ctlval.value() & (1 << 11); ctx.ctlval.value() &= ~(1 << 11); }; break; case 6: // SGT skip if greater than inst.ef = [](auto &ctx) { std::cerr << "unimplemented SGT\n"; assert(false); }; break; case 7: // CAF clear all flags inst.ef = [](auto &ctx) { std::cerr << "unimplemented CAF\n"; assert(false); }; break; } break; case 003: // KEYBOARD switch (bits & 07) { case 1: // KSF skip if TTI flag is set inst.read_ctlreg = TT_BITS; inst.possibly_redirects = true; inst.ef = [](auto &ctx) { if (ctx.ctlval.value() & TTI_FLAG) ctx.next_pc = (ctx.next_pc & ~07777) | ((ctx.next_pc + 1) & 07777); }; break; case 5: // KIE set TT_FLAGS from the accumulator inst.need_read_acc = true; inst.write_ctlreg = TT_FLAGS; inst.ef = [](auto &ctx) { ctx.ctlval = ctx.acc.value(); }; break; case 6: // KRB transfer keyboard buffer to accumulator and clear TTI flag inst.read_ctlreg = TT_BITS; inst.need_write_acc = true; inst.write_ctlreg = TT_BITS; inst.ef = [](auto &ctx) { ctx.acc = (ctx.ctlval.value() & TTI_DATA) >> TTI_DATA_SHIFT; ctx.ctlval.value() &= ~TTI_FLAG; }; break; default: inst.ef = [bits](auto &ctx) { std::cerr << "unimplemented IOT KB suboperation " << (bits & 07) << "\n"; assert(false); }; } break; case 004: // TELETYPE TELEPRINTER/PUNCH switch (bits & 07) { case 0: // TFL set TTO flag inst.read_ctlreg = TT_BITS; inst.write_ctlreg = TT_BITS; inst.ef = [](auto &ctx) { ctx.ctlval.value() |= TTO_FLAG; }; break; case 1: // TSF skip if TTO flag is set inst.read_ctlreg = TT_BITS; inst.possibly_redirects = true; inst.ef = [](auto &ctx) { if (ctx.ctlval.value() & TTO_FLAG) ctx.next_pc = (ctx.next_pc & ~07777) | ((ctx.next_pc + 1) & 07777); }; break; case 2: // TCF clear TTO flag inst.read_ctlreg = TT_BITS; inst.write_ctlreg = TT_BITS; inst.ef = [](auto &ctx) { ctx.ctlval.value() &= ~TTO_FLAG; }; break; case 4: // TPC print to TTO inst.need_read_acc = true; inst.read_ctlreg = TT_BITS; inst.write_ctlreg = TT_BITS; inst.ef = [](auto &ctx) { auto &x = ctx.ctlval.value(); auto chr = ctx.acc.value(); x &= ~TTO_DATA; x |= (chr << TTO_DATA_SHIFT) & TTO_DATA; x |= TTO_TX; }; break; case 5: // TSK skip if TTO flag is set or TTI flag is set inst.read_ctlreg = TT_BITS; inst.possibly_redirects = true; inst.ef = [](auto &ctx) { if (ctx.ctlval.value() & (TTI_FLAG | TTO_FLAG)) ctx.next_pc = (ctx.next_pc & ~07777) | ((ctx.next_pc + 1) & 07777); }; break; case 6: // TLS print to TTO and clear TTO flag inst.need_read_acc = true; inst.read_ctlreg = TT_BITS; inst.write_ctlreg = TT_BITS; inst.ef = [](auto &ctx) { auto &x = ctx.ctlval.value(); auto chr = ctx.acc.value(); x &= ~TTO_FLAG & ~TTO_DATA; x |= (chr << TTO_DATA_SHIFT) & TTO_DATA; x |= TTO_TX; }; break; default: inst.ef = [bits](auto &ctx) { std::cerr << "unimplemented IOT TT suboperation " << (bits & 07) << "\n"; assert(false); }; } break; case 020: case 021: case 022: case 023: case 024: case 025: case 026: case 027: // MEMORY MANAGEMENT { auto field = (bits >> 3) & 07; switch (bits & 07) { case 01: // CDF change data field inst.write_ctlreg = FLAGS; inst.ef = [flags, field](auto &ctx) { ctx.ctlval = (flags & ~FLAG_DF) | (field << FLAG_DF_SHIFT); }; break; case 04: // RMF restore memory field inst.read_ctlreg = FLAGS_SAVED; inst.write_ctlreg = FLAGS; inst.ef = [flags](auto &ctx) { ctx.ctlval.value() &= FLAG_DF | FLAG_IF | FLAG_USER_MODE; ctx.ctlval.value() |= flags & ~(FLAG_DF | FLAG_IF | FLAG_USER_MODE); }; break; default: inst.ef = [bits, field](auto &ctx) { std::cerr << "unimplemented IOT MEMORY suboperation " << (bits & 07) << " upon field " << field << "\n"; assert(false); }; } } break; default: inst.ef = [bits](auto &ctx) { std::cerr << "unimplemented IOT device " << ((bits >> 6) & 07) << ((bits >> 3) & 07) << " suboperation " << (bits & 07) << "\n"; assert(false); }; } break; case 7: // OPR if ((bits & 0400) == 0000) { bool cla = bits & 0200; // CLA clear accumulator bool cll = bits & 0100; // CLL clear link bool cma = bits & 0040; // CMA invert accumulator bool cml = bits & 0020; // CML invert link bool rar = bits & 0010; // RAR rotate right bool ral = bits & 0004; // RAL rotate left bool bsw = bits & 0002; // BSW byte swap bool iac = bits & 0001; // IAC increment {link,accumulator} inst.need_read_acc = cma || rar || ral || bsw || iac; inst.need_read_link = cml || rar || ral || iac; inst.need_write_acc = cla || cma || rar || ral || bsw || iac; inst.need_write_link = cll || cml || rar || ral || iac; inst.ef = [cla, cll, cma, cml, rar, ral, bsw, iac](auto &ctx) { if (cla) ctx.acc = 0; if (cll) ctx.link = 0; if (cma) ctx.acc = ~ctx.acc.value() & 07777; if (cml) ctx.link = !ctx.link.value(); if (iac) { ctx.acc = (ctx.acc.value() + 1) & 07777; if (ctx.acc.value() == 0) ctx.link = !ctx.link.value(); } if (rar && !ral) { unsigned int x = (ctx.link.value() << 12) | ctx.acc.value(); x = (x >> 1) | ((x & 1) << 12); if (bsw) x = (x >> 1) | ((x & 1) << 12); ctx.link = x >> 12; ctx.acc = x & 07777; } if (ral && !rar) { unsigned int x = (ctx.link.value() << 12) | ctx.acc.value(); x = ((x << 1) & 017777) | (x >> 12); if (bsw) x = ((x << 1) & 017777) | (x >> 12); ctx.link = x >> 12; ctx.acc = x & 07777; } if (bsw && !(rar || ral)) ctx.acc = ((ctx.acc.value() & 00077) << 6) | (ctx.acc.value() >> 6); }; } else if ((bits & 0411) == 0400) { bool cla = bits & 0200; // CLA clear accumulator bool sma = bits & 0100; // SMA skip if accumulator negative bool sza = bits & 0040; // SZA skip if accumulator zero bool snl = bits & 0020; // SNL skip if link set bool osr = bits & 0004; // OSR bitwise or switches into accumulator bool hlt = bits & 0002; // HLT halt inst.need_read_acc = sma || sza; inst.need_read_link = snl; inst.need_write_acc = cla; if (hlt) inst.write_ctlreg = HALTED; inst.possibly_redirects = true; inst.ef = [cla, sma, sza, snl, osr, hlt](auto &ctx) { bool skip = false; if (sma && (ctx.acc.value() & 04000)) skip = true; if (sza && (ctx.acc.value() == 0)) skip = true; if (snl && ctx.link.value()) skip = true; if (cla) ctx.acc = 0; assert(!osr); if (hlt) ctx.ctlval = 1; if (skip) ctx.next_pc = (ctx.next_pc & ~07777) | ((ctx.next_pc + 1) & 07777); }; } else if ((bits & 0411) == 0410) { bool cla = bits & 0200; // CLA clear accumulator bool spa = bits & 0100; // SPA skip if accumulator positive bool sna = bits & 0040; // SNA skip if accumulator nonzero bool szl = bits & 0020; // SZL skip if link clear bool osr = bits & 0004; // OSR bitwise or switches into accumulator bool hlt = bits & 0002; // HLT halt inst.need_read_acc = spa || sna; inst.need_read_link = szl; inst.need_write_acc = cla; if (hlt) inst.write_ctlreg = HALTED; inst.possibly_redirects = true; inst.ef = [cla, spa, sna, szl, osr, hlt](auto &ctx) { bool skip = true; if (spa && (ctx.acc.value() & 04000)) skip = false; if (sna && (ctx.acc.value() == 0)) skip = false; if (szl && ctx.link.value()) skip = false; if (cla) ctx.acc = 0; assert(!osr); if (hlt) ctx.ctlval = 1; if (skip) ctx.next_pc = (ctx.next_pc & ~07777) | ((ctx.next_pc + 1) & 07777); }; } else if ((bits & 0401) == 0401) { bool cla = bits & 0200; // CLA clear accumulator bool mqa = bits & 0100; // MQA bitwise or MQ into accumulator bool mql = bits & 0020; // MQL copy accumulator to MQ bool extended_arith = bits & 0056; inst.need_read_acc = mqa || mql; inst.need_read_mq = mqa; inst.need_write_acc = cla || mqa; inst.need_write_mq = mql; inst.ef = [cla, mqa, mql, extended_arith](auto &ctx) { assert(!extended_arith); if (cla) ctx.acc = 0; auto new_acc = ctx.acc; auto new_mq = ctx.mq; if (mqa) new_acc = ctx.acc.value() | ctx.mq.value(); if (mql) new_mq = ctx.acc.value(); ctx.acc = new_acc; ctx.mq = new_mq; }; } else { inst.ef = [bits](auto &ctx) { std::cerr << "unimplemented OPR " << ((bits >> 6) & 07) << ((bits >> 3) & 07) << (bits & 07) << "\n"; assert(false); }; } break; } // Instructions with memory operands may be direct or indirect if (inst.need_exec_load || inst.need_exec_store || inst.possibly_redirects) { auto addr = (pc & 070000) | ((bits & 00200) ? (pc & 07600) : 0) | (bits & 00177); if (bits & 00400) { inst.need_indirect_load = true; inst.init_address = addr; } else { inst.final_address = addr; } } // Non-jump indirect memory operands may be autoincrementing depending on operand bits if (!inst.possibly_redirects && inst.need_indirect_load && ((inst.init_address.value() & 07770) == 00010)) inst.need_autoinc_store = true; return inst; }