diff options
Diffstat (limited to '')
| -rw-r--r-- | isa/checker.cpp | 44 | ||||
| -rw-r--r-- | isa/decode.cpp | 96 | ||||
| -rw-r--r-- | isa/isa.h | 44 |
3 files changed, 184 insertions, 0 deletions
diff --git a/isa/checker.cpp b/isa/checker.cpp new file mode 100644 index 0000000..cd802a8 --- /dev/null +++ b/isa/checker.cpp | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | #include <cassert> | ||
| 2 | |||
| 3 | #include "isa/isa.h" | ||
| 4 | |||
| 5 | void checker::execute() { | ||
| 6 | assert(!halt); | ||
| 7 | auto int_enable_delay = ctlregs[ctlreg::INT_ENABLE] >> 1; | ||
| 8 | if (ctlregs[ctlreg::INT_ENABLE] & 1) { | ||
| 9 | // check for interrupt | ||
| 10 | } | ||
| 11 | ctlregs[ctlreg::INT_ENABLE] = (int_enable_delay << 1) | int_enable_delay; | ||
| 12 | inst = decode(ctlregs[ctlreg::DATA_INSTRUCTION_FIELD_BUFFER], | ||
| 13 | pc, | ||
| 14 | mem.fetch(pc)); | ||
| 15 | auto next_pc = inst.next_pc; | ||
| 16 | if (inst.need_indirect_load) { | ||
| 17 | auto addr = mem.fetch(inst.init_address.value()); | ||
| 18 | if (inst.need_autoinc_store) | ||
| 19 | mem.store(*inst.init_address, (addr + 1) & 07777); | ||
| 20 | inst.final_address = addr; | ||
| 21 | } else { | ||
| 22 | assert(!inst.need_autoinc_store); | ||
| 23 | } | ||
| 24 | if (inst.need_exec_load) | ||
| 25 | inst.data = mem.fetch(inst.final_address.value()); | ||
| 26 | if (inst.need_read_acc) | ||
| 27 | inst.acc = acc; | ||
| 28 | if (inst.need_read_link) | ||
| 29 | inst.link = link; | ||
| 30 | if (inst.read_ctlreg.has_value()) | ||
| 31 | inst.ctlval = ctlregs[*inst.read_ctlreg]; | ||
| 32 | inst.execute(); | ||
| 33 | if (inst.need_write_acc) | ||
| 34 | acc = inst.acc.value(); | ||
| 35 | if (inst.need_write_link) | ||
| 36 | link = inst.link.value(); | ||
| 37 | if (inst.write_ctlreg.has_value()) | ||
| 38 | ctlregs[*inst.write_ctlreg] = inst.ctlval.value(); | ||
| 39 | if (inst.need_exec_store) | ||
| 40 | mem.store(inst.final_address.value(), inst.data.value()); | ||
| 41 | assert(inst.next_pc == next_pc || inst.possibly_redirects); | ||
| 42 | pc = inst.next_pc; | ||
| 43 | halt = inst.halt; | ||
| 44 | } | ||
diff --git a/isa/decode.cpp b/isa/decode.cpp new file mode 100644 index 0000000..8a85d41 --- /dev/null +++ b/isa/decode.cpp | |||
| @@ -0,0 +1,96 @@ | |||
| 1 | #include <cassert> | ||
| 2 | |||
| 3 | #include "isa/isa.h" | ||
| 4 | |||
| 5 | instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bits) | ||
| 6 | { | ||
| 7 | instruction_context inst; | ||
| 8 | |||
| 9 | auto df = dfifb >> 3; | ||
| 10 | auto ifb = dfifb & 00007; | ||
| 11 | |||
| 12 | inst.next_pc = (pc & ~07777) | ((pc + 1) & 07777); | ||
| 13 | |||
| 14 | switch (bits >> 9) { | ||
| 15 | case 0: // AND | ||
| 16 | inst.need_exec_load = true; | ||
| 17 | inst.need_read_acc = true; | ||
| 18 | inst.need_write_acc = true; | ||
| 19 | inst.ef = [](auto &ctx) { | ||
| 20 | ctx.acc = ctx.acc.value() & ctx.data.value(); | ||
| 21 | }; | ||
| 22 | break; | ||
| 23 | case 1: // TAD | ||
| 24 | inst.need_exec_load = true; | ||
| 25 | inst.need_read_acc = true; | ||
| 26 | inst.need_read_link = true; | ||
| 27 | inst.need_write_acc = true; | ||
| 28 | inst.need_write_link = true; | ||
| 29 | inst.ef = [](auto &ctx) { | ||
| 30 | unsigned int sum = (ctx.link.value() << 12) + ctx.acc.value() + ctx.data.value(); | ||
| 31 | ctx.link = (sum >> 12) & 1; | ||
| 32 | ctx.acc = sum & 07777; | ||
| 33 | }; | ||
| 34 | break; | ||
| 35 | case 2: // ISZ | ||
| 36 | inst.need_exec_load = true; | ||
| 37 | inst.need_exec_store = true; | ||
| 38 | inst.possibly_redirects = true; | ||
| 39 | inst.ef = [](auto &ctx) { | ||
| 40 | ctx.data = (ctx.data.value() + 1) & 07777; | ||
| 41 | if (*ctx.data) | ||
| 42 | ctx.next_pc = (ctx.next_pc & ~07777) | ((ctx.next_pc + 1) & 07777); | ||
| 43 | }; | ||
| 44 | break; | ||
| 45 | case 3: // DCA | ||
| 46 | inst.need_read_acc = true; | ||
| 47 | inst.need_write_acc = true; | ||
| 48 | inst.need_exec_store = true; | ||
| 49 | inst.ef = [](auto &ctx) { | ||
| 50 | ctx.data = ctx.acc.value(); | ||
| 51 | ctx.acc = 0; | ||
| 52 | }; | ||
| 53 | break; | ||
| 54 | case 4: // JMS | ||
| 55 | inst.need_exec_store = true; | ||
| 56 | inst.possibly_redirects = true; | ||
| 57 | inst.ef = [ifb](auto &ctx) { | ||
| 58 | ctx.data = ctx.next_pc; | ||
| 59 | ctx.next_pc = (ifb << 12) | ((ctx.final_address.value() + 1) & 07777); | ||
| 60 | }; | ||
| 61 | break; | ||
| 62 | case 5: // JMP | ||
| 63 | inst.possibly_redirects = true; | ||
| 64 | inst.ef = [ifb](auto &ctx) { | ||
| 65 | ctx.next_pc = (ifb << 12) | (ctx.final_address.value() & 07777); | ||
| 66 | }; | ||
| 67 | break; | ||
| 68 | case 6: // IOT | ||
| 69 | inst.ef = [bits](auto &ctx) { | ||
| 70 | assert(false); | ||
| 71 | }; | ||
| 72 | break; | ||
| 73 | case 7: // OPR | ||
| 74 | inst.ef = [bits](auto &ctx) { | ||
| 75 | assert(false); | ||
| 76 | }; | ||
| 77 | break; | ||
| 78 | } | ||
| 79 | |||
| 80 | // Instructions with memory operands may be direct or indirect | ||
| 81 | if (inst.need_exec_load || inst.need_exec_store || inst.possibly_redirects) { | ||
| 82 | auto addr = (df << 12) | ((bits & 00200) ? (next_pc & 07600) : 0) | (bits & 00177); | ||
| 83 | if (bits & 00400) { | ||
| 84 | inst.need_indirect_load = true; | ||
| 85 | inst.init_address = addr; | ||
| 86 | } else { | ||
| 87 | inst.final_address = addr; | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | // Non-jump indirect memory operands may be autoincrementing depending on operand bits | ||
| 92 | if (!inst.possibly_redirects && inst.need_indirect_load && ((bits & 00170) == 00010)) | ||
| 93 | inst.need_autoinc_store = true; | ||
| 94 | |||
| 95 | return inst; | ||
| 96 | } | ||
diff --git a/isa/isa.h b/isa/isa.h new file mode 100644 index 0000000..3effb5b --- /dev/null +++ b/isa/isa.h | |||
| @@ -0,0 +1,44 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <functional> | ||
| 4 | #include <optional> | ||
| 5 | |||
| 6 | #include "infra/pipetrace.h" | ||
| 7 | |||
| 8 | enum class ctlreg { | ||
| 9 | DATA_INSTRUCTION_FIELD_BUFFER, // (df << 3) | if_buffer | ||
| 10 | DATA_INSTRUCTION_FIELD_SAVED, // (df_saved << 3) | if_saved | ||
| 11 | INT_ENABLE, // (int_enable_delay << 1) | int_enable | ||
| 12 | }; | ||
| 13 | |||
| 14 | struct instruction_context { | ||
| 15 | infra::transaction transaction; | ||
| 16 | |||
| 17 | // Known statically at decode time | ||
| 18 | bool need_indirect_load = false; // final_address = mem[init_address] | ||
| 19 | bool need_autoinc_store = false; // mem[init_address] += 1 | ||
| 20 | bool need_exec_load = false; // data = mem[final_address] | ||
| 21 | bool need_read_acc = false; // acc = %acc | ||
| 22 | bool need_read_link = false; // link = %link | ||
| 23 | std::optional<ctlreg> read_ctlreg; // ctlval = %[read_ctlreg] | ||
| 24 | bool need_write_acc = false; // %acc = acc | ||
| 25 | bool need_write_link = false; // %link = link | ||
| 26 | std::optional<ctlreg> write_ctlreg; // %[write_ctlreg] = ctlval | ||
| 27 | bool need_exec_store = false; // mem[final_address] = data | ||
| 28 | bool possibly_redirects = false; // %pc = next_pc | ||
| 29 | |||
| 30 | std::function<void(instruction_context &ctx)> ef; | ||
| 31 | void execute() { ef(*this); } | ||
| 32 | |||
| 33 | // May change over the lifetime of the instruction execution | ||
| 34 | unsigned int next_pc; // includes IF | ||
| 35 | std::optional<unsigned int> init_address; // includes DF | ||
| 36 | std::optional<unsigned int> final_address; // includes DF | ||
| 37 | std::optional<unsigned int> ctlval; | ||
| 38 | std::optional<unsigned int> data; | ||
| 39 | std::optional<unsigned int> acc; | ||
| 40 | std::optional<bool> link; | ||
| 41 | bool halt = false; | ||
| 42 | }; | ||
| 43 | |||
| 44 | instruction_context decode(unsigned int df, unsigned int pc, unsigned int bits); | ||
