From 68bdebd8cae39c30acc384664faa136aeaa9bb84 Mon Sep 17 00:00:00 2001 From: Julian Blake Kongslie Date: Sat, 5 Nov 2022 16:59:17 -0700 Subject: Add initial uarch model --- isa/checker.cpp | 97 ++++++++++++++++++------------------ isa/checker.h | 10 ++-- isa/decode.cpp | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- isa/isa.h | 10 ++++ 4 files changed, 213 insertions(+), 54 deletions(-) (limited to 'isa') diff --git a/isa/checker.cpp b/isa/checker.cpp index c6ab161..70b0f88 100644 --- a/isa/checker.cpp +++ b/isa/checker.cpp @@ -3,56 +3,55 @@ #include "isa/checker.h" #include "isa/isa.h" -void checker::execute() { - bool interrupt = system.interact(ctlregs); - - if (ctlregs[HALTED]) - return; - - inst = decode(ctlregs[FLAGS], - pc, - mem.fetch(pc), - interrupt); - auto next_pc = inst.next_pc; - - if (inst.need_indirect_load) { - auto addr = mem.fetch(inst.init_address.value()); - if (inst.need_autoinc_store) { - addr = (addr + 1) & 07777; - mem.store(*inst.init_address, addr); +void funcchecker::execute() { + if (!ctlregs[HALTED]) { + inst = decode(ctlregs[FLAGS], + pc, + mem.fetch(pc), + interrupt); + auto next_pc = inst.next_pc; + + if (inst.need_indirect_load) { + auto addr = mem.fetch(inst.init_address.value()); + if (inst.need_autoinc_store) { + addr = (addr + 1) & 07777; + mem.store(*inst.init_address, addr); + } + auto df = (ctlregs[FLAGS] & FLAG_DF) >> FLAG_DF_SHIFT; + inst.final_address = (df << 12) | addr; + } else { + assert(!inst.need_autoinc_store); } - auto df = (ctlregs[FLAGS] & FLAG_DF) >> FLAG_DF_SHIFT; - inst.final_address = (df << 12) | addr; - } else { - assert(!inst.need_autoinc_store); + + if (inst.need_exec_load) + inst.data = mem.fetch(inst.final_address.value()); + + if (inst.need_read_acc) + inst.acc = acc; + if (inst.need_read_link) + inst.link = link; + if (inst.need_read_mq) + inst.mq = mq; + if (inst.read_ctlreg.has_value()) + inst.ctlval = ctlregs[*inst.read_ctlreg]; + + inst.execute(); + + if (inst.need_write_acc) + acc = inst.acc.value(); + if (inst.need_write_link) + link = inst.link.value(); + if (inst.need_write_mq) + mq = inst.mq.value(); + if (inst.write_ctlreg.has_value()) + ctlregs[*inst.write_ctlreg] = inst.ctlval.value(); + + if (inst.need_exec_store) + mem.store(inst.final_address.value(), inst.data.value()); + + assert(inst.next_pc == next_pc || inst.possibly_redirects); + pc = inst.next_pc; } - if (inst.need_exec_load) - inst.data = mem.fetch(inst.final_address.value()); - - if (inst.need_read_acc) - inst.acc = acc; - if (inst.need_read_link) - inst.link = link; - if (inst.need_read_mq) - inst.mq = mq; - if (inst.read_ctlreg.has_value()) - inst.ctlval = ctlregs[*inst.read_ctlreg]; - - inst.execute(); - - if (inst.need_write_acc) - acc = inst.acc.value(); - if (inst.need_write_link) - link = inst.link.value(); - if (inst.need_write_mq) - mq = inst.mq.value(); - if (inst.write_ctlreg.has_value()) - ctlregs[*inst.write_ctlreg] = inst.ctlval.value(); - - if (inst.need_exec_store) - mem.store(inst.final_address.value(), inst.data.value()); - - assert(inst.next_pc == next_pc || inst.possibly_redirects); - pc = inst.next_pc; + interrupt = system.interact(icount++, ctlregs, true); } diff --git a/isa/checker.h b/isa/checker.h index 65ef8c9..30a8356 100644 --- a/isa/checker.h +++ b/isa/checker.h @@ -37,16 +37,18 @@ struct funcmem { } }; -struct checker { +struct funcchecker { unsigned int acc = 0; unsigned int link = 0; unsigned int mq = 0; - unsigned int pc = 00200; + unsigned int pc = 000200; std::array ctlregs; + std::uint64_t icount = 0; + bool interrupt = false; iomodel &system; instruction_context inst; funcmem mem; - checker(iomodel &system) + funcchecker(iomodel &system) : system(system) { ctlregs.fill(0); @@ -54,6 +56,6 @@ struct checker { } void execute(); bool done() { - return ctlregs[HALTED] && system.done(); + return ctlregs[HALTED] && system.done(icount); } }; diff --git a/isa/decode.cpp b/isa/decode.cpp index 745e529..2151e2b 100644 --- a/isa/decode.cpp +++ b/isa/decode.cpp @@ -1,9 +1,78 @@ #include #include +#include #include +#include #include "isa/isa.h" +void init_disasm_tables() +{ + for (unsigned int i = 0; i < 0377; ++i) { + auto &s = opr_disasm_group1[i]; + bool cla = i & 0200; + bool cll = i & 0100; + bool cma = i & 0040; + bool cml = i & 0020; + bool rar = i & 0010; + bool ral = i & 0004; + bool bsw = i & 0002; + bool iac = i & 0001; + if (cla) s += "CLA "; + if (cll) s += "CLL "; + if (cma) s += "CMA "; + if (cml) s += "CML "; + if (iac) s += "IAC "; + if (rar) s += "RAR "; + if (ral) s += "RAL "; + if (bsw) s += "BSW "; + if (s.size()) + s = s.substr(0, s.size() - 1); + else + s = "NOP"; + } + + for (unsigned int i = 0; i < 0366; ++i) { + auto &s = opr_disasm_group2_pos[i]; + bool cla = i & 0200; + bool sma = i & 0100; + bool sza = i & 0040; + bool snl = i & 0020; + bool osr = i & 0004; + bool hlt = i & 0002; + if (sma) s += "SMA "; + if (sza) s += "SZA "; + if (snl) s += "SNL "; + if (cla) s += "CLA "; + if (osr) s += "OSR "; + if (hlt) s += "HLT "; + if (s.size()) + s = s.substr(0, s.size() - 1); + else + s = "NOP"; + } + + for (unsigned int i = 0; i < 0366; ++i) { + auto &s = opr_disasm_group2_neg[i]; + bool cla = i & 0200; + bool spa = i & 0100; + bool sna = i & 0040; + bool szl = i & 0020; + bool osr = i & 0004; + bool hlt = i & 0002; + if (spa) s += "SPA "; + if (sna) s += "SNA "; + if (szl) s += "SZL "; + if (cla) s += "CLA "; + if (osr) s += "OSR "; + if (hlt) s += "HLT "; + if (s.size()) + s = s.substr(0, s.size() - 1); + else + s = "SKP"; + } +} + instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned int bits, bool interrupt) { #pragma GCC diagnostic push @@ -19,6 +88,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i #pragma GCC diagnostic pop instruction_context inst; + inst.bits = bits; inst.next_pc = (pc & ~07777) | ((pc + 1) & 07777); if (interrupt) { @@ -34,6 +104,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i inst.need_exec_load = true; inst.need_read_acc = true; inst.need_write_acc = true; + inst.df = "AND %a"; inst.ef = [](auto &ctx) { ctx.acc = ctx.acc.value() & ctx.data.value() & 07777; }; @@ -44,6 +115,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i inst.need_read_link = true; inst.need_write_acc = true; inst.need_write_link = true; + inst.df = "TAD %a"; inst.ef = [](auto &ctx) { unsigned int sum = ctx.acc.value() + ctx.data.value(); ctx.link = ctx.link.value() ^ (sum >> 12); @@ -54,6 +126,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i inst.need_exec_load = true; inst.need_exec_store = true; inst.possibly_redirects = true; + inst.df = "ISZ %a"; inst.ef = [](auto &ctx) { ctx.data = (ctx.data.value() + 1) & 07777; if (*ctx.data == 0) @@ -64,6 +137,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i inst.need_read_acc = true; inst.need_write_acc = true; inst.need_exec_store = true; + inst.df = "DCA %a"; inst.ef = [](auto &ctx) { ctx.data = ctx.acc.value(); ctx.acc = 0; @@ -72,6 +146,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i case 4: // JMS jump subroutine inst.need_exec_store = true; inst.possibly_redirects = true; + inst.df = "JMS %a"; inst.ef = [ifb](auto &ctx) { ctx.data = ctx.next_pc; ctx.next_pc = (ifb << 12) | ((ctx.final_address.value() + 1) & 07777); @@ -79,6 +154,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i break; case 5: // JMP jump inst.possibly_redirects = true; + inst.df = "JMP %a"; inst.ef = [ifb](auto &ctx) { ctx.next_pc = (ifb << 12) | (ctx.final_address.value() & 07777); }; @@ -91,6 +167,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i case 0: // SKON skip if interrupts enabled inst.possibly_redirects = true; + inst.df = "SKON"; inst.ef = [ie](auto &ctx) { if (ie) ctx.next_pc = (ctx.next_pc & 07777) | ((ctx.next_pc + 1) & 07777); @@ -99,6 +176,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i case 1: // ION set int_enable_delay inst.write_ctlreg = FLAGS; + inst.df = "ION"; inst.ef = [flags](auto &ctx) { ctx.ctlval = flags | FLAG_INT_ENABLE_DELAY; }; @@ -106,6 +184,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i case 2: // IOF clear int_enable and int_enable_delay inst.write_ctlreg = FLAGS; + inst.df = "IOF"; inst.ef = [flags](auto &ctx) { ctx.ctlval = flags & ~FLAG_INT_ENABLE_DELAY & ~FLAG_INT_ENABLE; }; @@ -113,6 +192,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i case 3: // SRQ skip if pending interrupt inst.possibly_redirects = true; + inst.df = "SRQ"; inst.ef = [ir](auto &ctx) { if (ir) ctx.next_pc = (ctx.next_pc & 07777) | ((ctx.next_pc + 1) & 07777); @@ -122,6 +202,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i // GTF get flags inst.read_ctlreg = FLAGS_SAVED; inst.need_read_link = true; + inst.df = "GTF"; inst.ef = [](auto &ctx) { auto flags = ctx.ctlval.value(); flags |= (unsigned int)ctx.link.value() << 11; @@ -133,6 +214,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i inst.need_read_acc = true; inst.need_write_link = true; inst.write_ctlreg = FLAGS; + inst.df = "RTF"; inst.ef = [ie](auto &ctx) { if (ie && (ctx.acc.value() & FLAG_INT_ENABLE)) { ctx.ctlval = ctx.acc.value() | FLAG_INT_ENABLE_DELAY; @@ -147,6 +229,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i break; case 6: // SGT skip if greater than + inst.df = "SGT"; inst.ef = [](auto &ctx) { std::cerr << "unimplemented SGT\n"; assert(false); @@ -154,6 +237,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i break; case 7: // CAF clear all flags + inst.df = "CAF"; inst.ef = [](auto &ctx) { std::cerr << "unimplemented CAF\n"; assert(false); @@ -168,6 +252,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i // KSF skip if TTI flag is set inst.read_ctlreg = TT_BITS; inst.possibly_redirects = true; + inst.df = "KSF"; inst.ef = [](auto &ctx) { if (ctx.ctlval.value() & TTI_FLAG) ctx.next_pc = (ctx.next_pc & ~07777) | ((ctx.next_pc + 1) & 07777); @@ -177,6 +262,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i // KIE set TT_FLAGS from the accumulator inst.need_read_acc = true; inst.write_ctlreg = TT_FLAGS; + inst.df = "KIE"; inst.ef = [](auto &ctx) { ctx.ctlval = ctx.acc.value(); }; @@ -186,12 +272,14 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i inst.read_ctlreg = TT_BITS; inst.need_write_acc = true; inst.write_ctlreg = TT_BITS; + inst.df = "KRB"; inst.ef = [](auto &ctx) { ctx.acc = (ctx.ctlval.value() & TTI_DATA) >> TTI_DATA_SHIFT; ctx.ctlval.value() &= ~TTI_FLAG; }; break; default: + inst.df = "IOT %b"; inst.ef = [bits](auto &ctx) { std::cerr << "unimplemented IOT KB suboperation " << (bits & 07) << "\n"; assert(false); @@ -205,6 +293,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i // TFL set TTO flag inst.read_ctlreg = TT_BITS; inst.write_ctlreg = TT_BITS; + inst.df = "TFL"; inst.ef = [](auto &ctx) { ctx.ctlval.value() |= TTO_FLAG; }; @@ -213,6 +302,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i // TSF skip if TTO flag is set inst.read_ctlreg = TT_BITS; inst.possibly_redirects = true; + inst.df = "TSF"; inst.ef = [](auto &ctx) { if (ctx.ctlval.value() & TTO_FLAG) ctx.next_pc = (ctx.next_pc & ~07777) | ((ctx.next_pc + 1) & 07777); @@ -222,6 +312,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i // TCF clear TTO flag inst.read_ctlreg = TT_BITS; inst.write_ctlreg = TT_BITS; + inst.df = "TCF"; inst.ef = [](auto &ctx) { ctx.ctlval.value() &= ~TTO_FLAG; }; @@ -231,6 +322,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i inst.need_read_acc = true; inst.read_ctlreg = TT_BITS; inst.write_ctlreg = TT_BITS; + inst.df = "TPC"; inst.ef = [](auto &ctx) { auto &x = ctx.ctlval.value(); auto chr = ctx.acc.value(); @@ -243,6 +335,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i // TSK skip if TTO flag is set or TTI flag is set inst.read_ctlreg = TT_BITS; inst.possibly_redirects = true; + inst.df = "TSK"; inst.ef = [](auto &ctx) { if (ctx.ctlval.value() & (TTI_FLAG | TTO_FLAG)) ctx.next_pc = (ctx.next_pc & ~07777) | ((ctx.next_pc + 1) & 07777); @@ -253,6 +346,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i inst.need_read_acc = true; inst.read_ctlreg = TT_BITS; inst.write_ctlreg = TT_BITS; + inst.df = "TLS"; inst.ef = [](auto &ctx) { auto &x = ctx.ctlval.value(); auto chr = ctx.acc.value(); @@ -262,6 +356,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i }; break; default: + inst.df = "IOT %b"; inst.ef = [bits](auto &ctx) { std::cerr << "unimplemented IOT TT suboperation " << (bits & 07) << "\n"; assert(false); @@ -283,6 +378,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i case 01: // CDF change data field inst.write_ctlreg = FLAGS; + inst.df = "CDF %f"; inst.ef = [flags, field](auto &ctx) { ctx.ctlval = (flags & ~FLAG_DF) | (field << FLAG_DF_SHIFT); }; @@ -293,12 +389,14 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i // RMF restore memory field inst.read_ctlreg = FLAGS_SAVED; inst.write_ctlreg = FLAGS; + inst.df = "RMF"; 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.df = "IOT %b"; inst.ef = [bits, field](auto &ctx) { std::cerr << "unimplemented IOT MEMORY suboperation " << (bits & 07) << " upon field " << field << "\n"; assert(false); @@ -306,6 +404,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i } break; default: + inst.df = "IOT %b"; inst.ef = [bits, field](auto &ctx) { std::cerr << "unimplemented IOT MEMORY suboperation " << (bits & 07) << " upon field " << field << "\n"; assert(false); @@ -314,6 +413,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i } break; default: + inst.df = "IOT %b"; inst.ef = [bits](auto &ctx) { std::cerr << "warning: unimplemented IOT device " << ((bits >> 6) & 07) << ((bits >> 3) & 07) << " suboperation " << (bits & 07) << "\n"; }; @@ -333,6 +433,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i 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.df = opr_disasm_group1[bits & 0377].c_str(); inst.ef = [cla, cll, cma, cml, rar, ral, bsw, iac](auto &ctx) { if (cla) ctx.acc = 0; if (cll) ctx.link = 0; @@ -374,6 +475,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i if (hlt) inst.write_ctlreg = HALTED; inst.possibly_redirects = true; + inst.df = opr_disasm_group2_pos[bits & 0366].c_str(); inst.ef = [cla, sma, sza, snl, osr, hlt](auto &ctx) { bool skip = false; if (sma && (ctx.acc.value() & 04000)) skip = true; @@ -398,6 +500,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i if (hlt) inst.write_ctlreg = HALTED; inst.possibly_redirects = true; + inst.df = opr_disasm_group2_neg[bits & 0366].c_str(); inst.ef = [cla, spa, sna, szl, osr, hlt](auto &ctx) { bool skip = true; if (spa && (ctx.acc.value() & 04000)) skip = false; @@ -418,6 +521,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i inst.need_read_mq = mqa; inst.need_write_acc = cla || mqa; inst.need_write_mq = mql; + inst.df = opr_disasm_extended_arith[bits & 0376].c_str(); inst.ef = [cla, mqa, mql, extended_arith](auto &ctx) { assert(!extended_arith); if (cla) ctx.acc = 0; @@ -429,6 +533,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i ctx.mq = new_mq; }; } else { + inst.df = "OPR %b"; inst.ef = [bits](auto &ctx) { std::cerr << "unimplemented OPR " << ((bits >> 6) & 07) << ((bits >> 3) & 07) << (bits & 07) << "\n"; assert(false); @@ -438,7 +543,7 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i } // Instructions with memory operands may be direct or indirect - if (inst.need_exec_load || inst.need_exec_store || inst.possibly_redirects) { + if (inst.need_exec_load || inst.need_exec_store || (bits & 06000) == 04000 /* JMS and JMP */) { auto addr = (pc & 070000) | ((bits & 00200) ? (pc & 07600) : 0) | (bits & 00177); if (bits & 00400) { inst.need_indirect_load = true; @@ -454,3 +559,46 @@ instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned i return inst; } + +std::string instruction_context::disasm() const +{ + if (!df) + return ""; + + std::ostringstream s; + for (const char *p = df; *p; ++p) { + if (*p != '%') { + s << *p; + continue; + } + switch (*++p) { + case '\0': + case '%': + s << '%'; + break; + case 'a': + if (bits & 00400) + s << "I "; + if (~bits & 00200) + s << "Z "; + s << fmt::format("{:03o}", bits & 00177); + break; + case 'b': + s << fmt::format("{:04o}", bits); + break; + case 'f': + s << fmt::format("{:01o}", (bits & 00070) >> 3); + break; + default: + s << '%' << *p; + break; + } + } + + if (init_address.has_value()) + s << fmt::format(" [{:05o}] ->", *init_address); + if (final_address.has_value()) + s << fmt::format(" [{:05o}]", *final_address); + + return s.str(); +} diff --git a/isa/isa.h b/isa/isa.h index 8156c08..4083e16 100644 --- a/isa/isa.h +++ b/isa/isa.h @@ -51,6 +51,11 @@ static constexpr std::uint_fast32_t TTO_DATA = 0xff << TTO_DATA_SHIFT; static constexpr std::uint_fast32_t TTF_INT_ENABLE = 1 << 0; static constexpr std::uint_fast32_t TTF_STATUS_ENABLE = 1 << 1; +static std::string opr_disasm_group1[0377]; +static std::string opr_disasm_group2_pos[0366]; +static std::string opr_disasm_group2_neg[0366]; +static std::string opr_disasm_extended_arith[0376]; + struct instruction_context { // Known statically at decode time bool need_indirect_load = false; // final_address = mem[init_address] @@ -67,10 +72,14 @@ struct instruction_context { bool need_exec_store = false; // mem[final_address] = data bool possibly_redirects = false; // %pc = next_pc + const char *df = nullptr; + std::string disasm() const; + std::function ef; void execute() { ef(*this); } // May change over the lifetime of the instruction execution + unsigned int bits; unsigned int next_pc; // includes IF std::optional init_address; // includes DF std::optional final_address; // includes DF @@ -81,4 +90,5 @@ struct instruction_context { std::optional mq; }; +void init_disasm_tables(); instruction_context decode(std::uint_fast32_t flags, unsigned int pc, unsigned int bits, bool interrupt); -- cgit v1.2.3