diff options
Diffstat (limited to '')
| -rw-r--r-- | io/event.h | 18 | ||||
| -rw-r--r-- | io/model.cpp | 49 | ||||
| -rw-r--r-- | io/model.h | 15 | ||||
| -rw-r--r-- | isa/checker.cpp | 22 | ||||
| -rw-r--r-- | isa/checker.h | 6 | ||||
| -rw-r--r-- | isa/decode.cpp | 20 | ||||
| -rw-r--r-- | isa/isa.h | 12 | ||||
| -rw-r--r-- | main.cpp | 12 |
8 files changed, 126 insertions, 28 deletions
diff --git a/io/event.h b/io/event.h new file mode 100644 index 0000000..02f3fab --- /dev/null +++ b/io/event.h | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <map> | ||
| 4 | |||
| 5 | #include "isa/isa.h" | ||
| 6 | |||
| 7 | struct event { | ||
| 8 | ctlreg reg; | ||
| 9 | unsigned int mask; | ||
| 10 | unsigned int value; | ||
| 11 | event(ctlreg reg, unsigned int value, unsigned int mask=~0) | ||
| 12 | : reg(reg) | ||
| 13 | , mask(mask) | ||
| 14 | , value(value) | ||
| 15 | { } | ||
| 16 | }; | ||
| 17 | |||
| 18 | typedef std::multimap<std::uint64_t, event> event_log; | ||
diff --git a/io/model.cpp b/io/model.cpp new file mode 100644 index 0000000..dd55ce6 --- /dev/null +++ b/io/model.cpp | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | #include <array> | ||
| 2 | #include <cstdint> | ||
| 3 | #include <iostream> | ||
| 4 | #include <utility> | ||
| 5 | |||
| 6 | #include "io/model.h" | ||
| 7 | #include "isa/isa.h" | ||
| 8 | |||
| 9 | bool iomodel::interact(std::array<unsigned int, NUM_CTLREGS> &ctlregs) { | ||
| 10 | auto [ebegin, eend] = log.equal_range(time); | ||
| 11 | for (auto e = ebegin; e != eend; ++e) { | ||
| 12 | auto &r = ctlregs[e->second.reg]; | ||
| 13 | r &= ~e->second.mask; | ||
| 14 | r |= e->second.value; | ||
| 15 | } | ||
| 16 | |||
| 17 | ++time; | ||
| 18 | |||
| 19 | if (ctlregs[TT_OUTPUT] & 0x100) { | ||
| 20 | // PDP-8 doesn't really have support for 8-bit output, this is Jules' contribution | ||
| 21 | std::cout << (char)((ctlregs[TT_OUTPUT] & 0xff) ^ 0x80); | ||
| 22 | ctlregs[TT_OUTPUT] &= ~0x1ff; | ||
| 23 | log.emplace(time + TT_OUTPUT_DELAY, event(TT_OUTPUT, 0x200, 0)); | ||
| 24 | } | ||
| 25 | |||
| 26 | bool interrupt = false; | ||
| 27 | if (ctlregs[INT_ENABLE] & 1) { | ||
| 28 | if (ctlregs[TT_INPUT_INT_ENABLE]) { | ||
| 29 | if (ctlregs[TT_INPUT] & 0x100) | ||
| 30 | interrupt = true; | ||
| 31 | } | ||
| 32 | if (ctlregs[TT_OUTPUT_INT_ENABLE]) { | ||
| 33 | if (!(ctlregs[TT_OUTPUT] & 0x400) && (ctlregs[TT_OUTPUT] & 0x200)) | ||
| 34 | interrupt = true; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | if (interrupt) { | ||
| 39 | ctlregs[DATA_INSTRUCTION_FIELD_SAVED] = ctlregs[DATA_INSTRUCTION_FIELD_BUFFER]; | ||
| 40 | ctlregs[DATA_INSTRUCTION_FIELD_BUFFER] = 0; | ||
| 41 | ctlregs[HALTED] = 0; | ||
| 42 | ctlregs[INT_ENABLE] = 0; | ||
| 43 | ctlregs[TT_OUTPUT] = (ctlregs[TT_OUTPUT] & 0x200) * 3; | ||
| 44 | } else { | ||
| 45 | ctlregs[INT_ENABLE] = (ctlregs[INT_ENABLE] >> 1) * 3; | ||
| 46 | } | ||
| 47 | |||
| 48 | return interrupt; | ||
| 49 | }; | ||
diff --git a/io/model.h b/io/model.h new file mode 100644 index 0000000..86fb086 --- /dev/null +++ b/io/model.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <array> | ||
| 4 | #include <cstdint> | ||
| 5 | |||
| 6 | #include "io/event.h" | ||
| 7 | #include "isa/isa.h" | ||
| 8 | |||
| 9 | struct iomodel { | ||
| 10 | static constexpr unsigned int TT_OUTPUT_DELAY = 100; | ||
| 11 | |||
| 12 | event_log log; | ||
| 13 | std::uint64_t time = 0; | ||
| 14 | bool interact(std::array<unsigned int, NUM_CTLREGS> &ctlregs); | ||
| 15 | }; | ||
diff --git a/isa/checker.cpp b/isa/checker.cpp index 1919bd1..7bca5c9 100644 --- a/isa/checker.cpp +++ b/isa/checker.cpp | |||
| @@ -4,16 +4,17 @@ | |||
| 4 | #include "isa/isa.h" | 4 | #include "isa/isa.h" |
| 5 | 5 | ||
| 6 | void checker::execute() { | 6 | void checker::execute() { |
| 7 | assert(!halted); | 7 | bool interrupt = system.interact(ctlregs); |
| 8 | auto int_enable_delay = ctlregs[INT_ENABLE] >> 1; | 8 | |
| 9 | if (ctlregs[INT_ENABLE] & 1) { | 9 | if (ctlregs[HALTED]) |
| 10 | // check for interrupt | 10 | return; |
| 11 | } | 11 | |
| 12 | ctlregs[INT_ENABLE] = (int_enable_delay << 1) | int_enable_delay; | ||
| 13 | inst = decode(ctlregs[DATA_INSTRUCTION_FIELD_BUFFER], | 12 | inst = decode(ctlregs[DATA_INSTRUCTION_FIELD_BUFFER], |
| 14 | pc, | 13 | pc, |
| 15 | mem.fetch(pc)); | 14 | mem.fetch(pc), |
| 15 | interrupt); | ||
| 16 | auto next_pc = inst.next_pc; | 16 | auto next_pc = inst.next_pc; |
| 17 | |||
| 17 | if (inst.need_indirect_load) { | 18 | if (inst.need_indirect_load) { |
| 18 | auto addr = mem.fetch(inst.init_address.value()); | 19 | auto addr = mem.fetch(inst.init_address.value()); |
| 19 | if (inst.need_autoinc_store) | 20 | if (inst.need_autoinc_store) |
| @@ -22,8 +23,10 @@ void checker::execute() { | |||
| 22 | } else { | 23 | } else { |
| 23 | assert(!inst.need_autoinc_store); | 24 | assert(!inst.need_autoinc_store); |
| 24 | } | 25 | } |
| 26 | |||
| 25 | if (inst.need_exec_load) | 27 | if (inst.need_exec_load) |
| 26 | inst.data = mem.fetch(inst.final_address.value()); | 28 | inst.data = mem.fetch(inst.final_address.value()); |
| 29 | |||
| 27 | if (inst.need_read_acc) | 30 | if (inst.need_read_acc) |
| 28 | inst.acc = acc; | 31 | inst.acc = acc; |
| 29 | if (inst.need_read_link) | 32 | if (inst.need_read_link) |
| @@ -32,7 +35,9 @@ void checker::execute() { | |||
| 32 | inst.mq = mq; | 35 | inst.mq = mq; |
| 33 | if (inst.read_ctlreg.has_value()) | 36 | if (inst.read_ctlreg.has_value()) |
| 34 | inst.ctlval = ctlregs[*inst.read_ctlreg]; | 37 | inst.ctlval = ctlregs[*inst.read_ctlreg]; |
| 38 | |||
| 35 | inst.execute(); | 39 | inst.execute(); |
| 40 | |||
| 36 | if (inst.need_write_acc) | 41 | if (inst.need_write_acc) |
| 37 | acc = inst.acc.value(); | 42 | acc = inst.acc.value(); |
| 38 | if (inst.need_write_link) | 43 | if (inst.need_write_link) |
| @@ -41,9 +46,10 @@ void checker::execute() { | |||
| 41 | mq = inst.mq.value(); | 46 | mq = inst.mq.value(); |
| 42 | if (inst.write_ctlreg.has_value()) | 47 | if (inst.write_ctlreg.has_value()) |
| 43 | ctlregs[*inst.write_ctlreg] = inst.ctlval.value(); | 48 | ctlregs[*inst.write_ctlreg] = inst.ctlval.value(); |
| 49 | |||
| 44 | if (inst.need_exec_store) | 50 | if (inst.need_exec_store) |
| 45 | mem.store(inst.final_address.value(), inst.data.value()); | 51 | mem.store(inst.final_address.value(), inst.data.value()); |
| 52 | |||
| 46 | assert(inst.next_pc == next_pc || inst.possibly_redirects); | 53 | assert(inst.next_pc == next_pc || inst.possibly_redirects); |
| 47 | pc = inst.next_pc; | 54 | pc = inst.next_pc; |
| 48 | halted = inst.halt; | ||
| 49 | } | 55 | } |
diff --git a/isa/checker.h b/isa/checker.h index d70997f..332c483 100644 --- a/isa/checker.h +++ b/isa/checker.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | #include <array> | 3 | #include <array> |
| 4 | #include <map> | 4 | #include <map> |
| 5 | 5 | ||
| 6 | #include "io/model.h" | ||
| 6 | #include "isa/isa.h" | 7 | #include "isa/isa.h" |
| 7 | 8 | ||
| 8 | struct funcmem { | 9 | struct funcmem { |
| @@ -41,10 +42,11 @@ struct checker { | |||
| 41 | unsigned int mq = 0; | 42 | unsigned int mq = 0; |
| 42 | unsigned int pc = 00200; | 43 | unsigned int pc = 00200; |
| 43 | std::array<unsigned int, NUM_CTLREGS> ctlregs; | 44 | std::array<unsigned int, NUM_CTLREGS> ctlregs; |
| 45 | iomodel &system; | ||
| 44 | instruction_context inst; | 46 | instruction_context inst; |
| 45 | bool halted = false; | ||
| 46 | funcmem mem; | 47 | funcmem mem; |
| 47 | checker() | 48 | checker(iomodel &system) |
| 49 | : system(system) | ||
| 48 | { | 50 | { |
| 49 | ctlregs.fill(0); | 51 | ctlregs.fill(0); |
| 50 | } | 52 | } |
diff --git a/isa/decode.cpp b/isa/decode.cpp index 1979982..d24632b 100644 --- a/isa/decode.cpp +++ b/isa/decode.cpp | |||
| @@ -2,15 +2,21 @@ | |||
| 2 | 2 | ||
| 3 | #include "isa/isa.h" | 3 | #include "isa/isa.h" |
| 4 | 4 | ||
| 5 | instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bits) | 5 | instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bits, bool interrupt) |
| 6 | { | 6 | { |
| 7 | instruction_context inst; | ||
| 8 | |||
| 9 | auto df = dfifb >> 3; | 7 | auto df = dfifb >> 3; |
| 10 | auto ifb = dfifb & 00007; | 8 | auto ifb = dfifb & 00007; |
| 11 | 9 | ||
| 10 | instruction_context inst; | ||
| 12 | inst.next_pc = (pc & ~07777) | ((pc + 1) & 07777); | 11 | inst.next_pc = (pc & ~07777) | ((pc + 1) & 07777); |
| 13 | 12 | ||
| 13 | if (interrupt) { | ||
| 14 | bits = 04000; | ||
| 15 | assert(df == 0); | ||
| 16 | assert(ifb == 0); | ||
| 17 | inst.next_pc = pc; | ||
| 18 | } | ||
| 19 | |||
| 14 | switch (bits >> 9) { | 20 | switch (bits >> 9) { |
| 15 | case 0: // AND | 21 | case 0: // AND |
| 16 | inst.need_exec_load = true; | 22 | inst.need_exec_load = true; |
| @@ -121,6 +127,8 @@ instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bit | |||
| 121 | inst.need_read_acc = sma || sza; | 127 | inst.need_read_acc = sma || sza; |
| 122 | inst.need_read_link = snl; | 128 | inst.need_read_link = snl; |
| 123 | inst.need_write_acc = cla; | 129 | inst.need_write_acc = cla; |
| 130 | if (hlt) | ||
| 131 | inst.write_ctlreg = HALTED; | ||
| 124 | inst.possibly_redirects = true; | 132 | inst.possibly_redirects = true; |
| 125 | inst.ef = [cla, sma, sza, snl, osr, hlt](auto &ctx) { | 133 | inst.ef = [cla, sma, sza, snl, osr, hlt](auto &ctx) { |
| 126 | bool skip = false; | 134 | bool skip = false; |
| @@ -129,7 +137,7 @@ instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bit | |||
| 129 | if (snl && ctx.link.value()) skip = true; | 137 | if (snl && ctx.link.value()) skip = true; |
| 130 | if (cla) ctx.acc = 0; | 138 | if (cla) ctx.acc = 0; |
| 131 | assert(!osr); | 139 | assert(!osr); |
| 132 | if (hlt) ctx.halt = true; | 140 | if (hlt) ctx.ctlval = 1; |
| 133 | if (skip) | 141 | if (skip) |
| 134 | ctx.next_pc = (ctx.next_pc & 070000) | ((ctx.next_pc + 1) & 007777); | 142 | ctx.next_pc = (ctx.next_pc & 070000) | ((ctx.next_pc + 1) & 007777); |
| 135 | }; | 143 | }; |
| @@ -143,6 +151,8 @@ instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bit | |||
| 143 | inst.need_read_acc = spa || sna; | 151 | inst.need_read_acc = spa || sna; |
| 144 | inst.need_read_link = szl; | 152 | inst.need_read_link = szl; |
| 145 | inst.need_write_acc = cla; | 153 | inst.need_write_acc = cla; |
| 154 | if (hlt) | ||
| 155 | inst.write_ctlreg = HALTED; | ||
| 146 | inst.possibly_redirects = true; | 156 | inst.possibly_redirects = true; |
| 147 | inst.ef = [cla, spa, sna, szl, osr, hlt](auto &ctx) { | 157 | inst.ef = [cla, spa, sna, szl, osr, hlt](auto &ctx) { |
| 148 | bool skip = true; | 158 | bool skip = true; |
| @@ -151,7 +161,7 @@ instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bit | |||
| 151 | if (szl && ctx.link.value()) skip = false; | 161 | if (szl && ctx.link.value()) skip = false; |
| 152 | if (cla) ctx.acc = 0; | 162 | if (cla) ctx.acc = 0; |
| 153 | assert(!osr); | 163 | assert(!osr); |
| 154 | if (hlt) ctx.halt = true; | 164 | if (hlt) ctx.ctlval = 1; |
| 155 | if (skip) | 165 | if (skip) |
| 156 | ctx.next_pc = (ctx.next_pc & 070000) | ((ctx.next_pc + 1) & 007777); | 166 | ctx.next_pc = (ctx.next_pc & 070000) | ((ctx.next_pc + 1) & 007777); |
| 157 | }; | 167 | }; |
| @@ -3,19 +3,20 @@ | |||
| 3 | #include <functional> | 3 | #include <functional> |
| 4 | #include <optional> | 4 | #include <optional> |
| 5 | 5 | ||
| 6 | #include "infra/pipetrace.h" | ||
| 7 | |||
| 8 | enum ctlreg { | 6 | enum ctlreg { |
| 9 | DATA_INSTRUCTION_FIELD_BUFFER, // (df << 3) | if_buffer | 7 | DATA_INSTRUCTION_FIELD_BUFFER, // (df << 3) | if_buffer |
| 10 | DATA_INSTRUCTION_FIELD_SAVED, // (df_saved << 3) | if_saved | 8 | DATA_INSTRUCTION_FIELD_SAVED, // (df_saved << 3) | if_saved |
| 9 | HALTED, | ||
| 11 | INT_ENABLE, // (int_enable_delay << 1) | int_enable | 10 | INT_ENABLE, // (int_enable_delay << 1) | int_enable |
| 11 | TT_INPUT, // (tti_flag << 8) | tti_buffer | ||
| 12 | TT_INPUT_INT_ENABLE, | ||
| 13 | TT_OUTPUT, // {tto_flag_old, tto_flag, tto_tx, tto_data} | ||
| 14 | TT_OUTPUT_INT_ENABLE, | ||
| 12 | 15 | ||
| 13 | NUM_CTLREGS, | 16 | NUM_CTLREGS, |
| 14 | }; | 17 | }; |
| 15 | 18 | ||
| 16 | struct instruction_context { | 19 | struct instruction_context { |
| 17 | infra::transaction transaction; | ||
| 18 | |||
| 19 | // Known statically at decode time | 20 | // Known statically at decode time |
| 20 | bool need_indirect_load = false; // final_address = mem[init_address] | 21 | bool need_indirect_load = false; // final_address = mem[init_address] |
| 21 | bool need_autoinc_store = false; // mem[init_address] += 1 | 22 | bool need_autoinc_store = false; // mem[init_address] += 1 |
| @@ -43,7 +44,6 @@ struct instruction_context { | |||
| 43 | std::optional<unsigned int> acc; | 44 | std::optional<unsigned int> acc; |
| 44 | std::optional<bool> link; | 45 | std::optional<bool> link; |
| 45 | std::optional<unsigned int> mq; | 46 | std::optional<unsigned int> mq; |
| 46 | bool halt = false; | ||
| 47 | }; | 47 | }; |
| 48 | 48 | ||
| 49 | instruction_context decode(unsigned int df, unsigned int pc, unsigned int bits); | 49 | instruction_context decode(unsigned int df, unsigned int pc, unsigned int bits, bool interrupt); |
| @@ -30,10 +30,8 @@ int load_program(checker &checker, const std::uint8_t *program) { | |||
| 30 | address = ((b1 & 0077) << 6) | *program++; | 30 | address = ((b1 & 0077) << 6) | *program++; |
| 31 | } else if ((b1 & 0300) == 0000) { | 31 | } else if ((b1 & 0300) == 0000) { |
| 32 | seen_non_leader = true; | 32 | seen_non_leader = true; |
| 33 | if (data.has_value()) { | 33 | if (data.has_value()) |
| 34 | //std::cout << fmt::format("mem[{:06o}] = {:04o}\n", data->first, data->second); | ||
| 35 | checker.mem.store(data->first, data->second); | 34 | checker.mem.store(data->first, data->second); |
| 36 | } | ||
| 37 | auto a = field | address++; | 35 | auto a = field | address++; |
| 38 | auto d = ((b1 & 0077) << 6) | *program++; | 36 | auto d = ((b1 & 0077) << 6) | *program++; |
| 39 | data = std::make_pair(a, d); | 37 | data = std::make_pair(a, d); |
| @@ -58,7 +56,8 @@ int main(int argc, const char *argv[]) { | |||
| 58 | return 1; | 56 | return 1; |
| 59 | } | 57 | } |
| 60 | 58 | ||
| 61 | checker checker; | 59 | iomodel system; |
| 60 | checker checker(system); | ||
| 62 | 61 | ||
| 63 | for (--argc, ++argv; argc; --argc, ++argv) { | 62 | for (--argc, ++argv; argc; --argc, ++argv) { |
| 64 | auto program = programs.at(argv[0]); | 63 | auto program = programs.at(argv[0]); |
| @@ -66,10 +65,9 @@ int main(int argc, const char *argv[]) { | |||
| 66 | return err; | 65 | return err; |
| 67 | } | 66 | } |
| 68 | 67 | ||
| 69 | while (!checker.halted) { | 68 | while (true) { |
| 70 | std::cout << fmt::format("{:04o}: ", checker.pc); | 69 | std::cout << fmt::format("{:9} @{:04o} {:01o}:{:04o}:{:04o}\n", system.time, checker.pc, checker.link, checker.acc, checker.mq); |
| 71 | checker.execute(); | 70 | checker.execute(); |
| 72 | std::cout << fmt::format("link={:01o} acc={:04o} mq={:04o}\n", checker.link, checker.acc, checker.mq); | ||
| 73 | } | 71 | } |
| 74 | 72 | ||
| 75 | return 0; | 73 | return 0; |
