From a59407a215d6112c2e20b1a746b33742209e5f87 Mon Sep 17 00:00:00 2001 From: Julian Blake Kongslie Date: Sun, 16 Oct 2022 16:24:49 -0700 Subject: Support for log-based event model --- io/event.h | 18 ++++++++++++++++++ io/model.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ io/model.h | 15 +++++++++++++++ isa/checker.cpp | 22 ++++++++++++++-------- isa/checker.h | 6 ++++-- isa/decode.cpp | 20 +++++++++++++++----- isa/isa.h | 12 ++++++------ main.cpp | 12 +++++------- 8 files changed, 126 insertions(+), 28 deletions(-) create mode 100644 io/event.h create mode 100644 io/model.cpp create mode 100644 io/model.h 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 @@ +#pragma once + +#include + +#include "isa/isa.h" + +struct event { + ctlreg reg; + unsigned int mask; + unsigned int value; + event(ctlreg reg, unsigned int value, unsigned int mask=~0) + : reg(reg) + , mask(mask) + , value(value) + { } +}; + +typedef std::multimap 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 @@ +#include +#include +#include +#include + +#include "io/model.h" +#include "isa/isa.h" + +bool iomodel::interact(std::array &ctlregs) { + auto [ebegin, eend] = log.equal_range(time); + for (auto e = ebegin; e != eend; ++e) { + auto &r = ctlregs[e->second.reg]; + r &= ~e->second.mask; + r |= e->second.value; + } + + ++time; + + if (ctlregs[TT_OUTPUT] & 0x100) { + // PDP-8 doesn't really have support for 8-bit output, this is Jules' contribution + std::cout << (char)((ctlregs[TT_OUTPUT] & 0xff) ^ 0x80); + ctlregs[TT_OUTPUT] &= ~0x1ff; + log.emplace(time + TT_OUTPUT_DELAY, event(TT_OUTPUT, 0x200, 0)); + } + + bool interrupt = false; + if (ctlregs[INT_ENABLE] & 1) { + if (ctlregs[TT_INPUT_INT_ENABLE]) { + if (ctlregs[TT_INPUT] & 0x100) + interrupt = true; + } + if (ctlregs[TT_OUTPUT_INT_ENABLE]) { + if (!(ctlregs[TT_OUTPUT] & 0x400) && (ctlregs[TT_OUTPUT] & 0x200)) + interrupt = true; + } + } + + if (interrupt) { + ctlregs[DATA_INSTRUCTION_FIELD_SAVED] = ctlregs[DATA_INSTRUCTION_FIELD_BUFFER]; + ctlregs[DATA_INSTRUCTION_FIELD_BUFFER] = 0; + ctlregs[HALTED] = 0; + ctlregs[INT_ENABLE] = 0; + ctlregs[TT_OUTPUT] = (ctlregs[TT_OUTPUT] & 0x200) * 3; + } else { + ctlregs[INT_ENABLE] = (ctlregs[INT_ENABLE] >> 1) * 3; + } + + return interrupt; +}; 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 @@ +#pragma once + +#include +#include + +#include "io/event.h" +#include "isa/isa.h" + +struct iomodel { + static constexpr unsigned int TT_OUTPUT_DELAY = 100; + + event_log log; + std::uint64_t time = 0; + bool interact(std::array &ctlregs); +}; 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 @@ #include "isa/isa.h" void checker::execute() { - assert(!halted); - auto int_enable_delay = ctlregs[INT_ENABLE] >> 1; - if (ctlregs[INT_ENABLE] & 1) { - // check for interrupt - } - ctlregs[INT_ENABLE] = (int_enable_delay << 1) | int_enable_delay; + bool interrupt = system.interact(ctlregs); + + if (ctlregs[HALTED]) + return; + inst = decode(ctlregs[DATA_INSTRUCTION_FIELD_BUFFER], pc, - mem.fetch(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) @@ -22,8 +23,10 @@ void checker::execute() { } 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) @@ -32,7 +35,9 @@ void checker::execute() { 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) @@ -41,9 +46,10 @@ void checker::execute() { 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; - halted = inst.halt; } 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 @@ #include #include +#include "io/model.h" #include "isa/isa.h" struct funcmem { @@ -41,10 +42,11 @@ struct checker { unsigned int mq = 0; unsigned int pc = 00200; std::array ctlregs; + iomodel &system; instruction_context inst; - bool halted = false; funcmem mem; - checker() + checker(iomodel &system) + : system(system) { ctlregs.fill(0); } 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 @@ #include "isa/isa.h" -instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bits) +instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bits, bool interrupt) { - instruction_context inst; - auto df = dfifb >> 3; auto ifb = dfifb & 00007; + 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; + } + switch (bits >> 9) { case 0: // AND inst.need_exec_load = true; @@ -121,6 +127,8 @@ instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bit 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; @@ -129,7 +137,7 @@ instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bit if (snl && ctx.link.value()) skip = true; if (cla) ctx.acc = 0; assert(!osr); - if (hlt) ctx.halt = true; + if (hlt) ctx.ctlval = 1; if (skip) ctx.next_pc = (ctx.next_pc & 070000) | ((ctx.next_pc + 1) & 007777); }; @@ -143,6 +151,8 @@ instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bit 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; @@ -151,7 +161,7 @@ instruction_context decode(unsigned int dfifb, unsigned int pc, unsigned int bit if (szl && ctx.link.value()) skip = false; if (cla) ctx.acc = 0; assert(!osr); - if (hlt) ctx.halt = true; + if (hlt) ctx.ctlval = 1; if (skip) ctx.next_pc = (ctx.next_pc & 070000) | ((ctx.next_pc + 1) & 007777); }; diff --git a/isa/isa.h b/isa/isa.h index 11a8db2..bea484e 100644 --- a/isa/isa.h +++ b/isa/isa.h @@ -3,19 +3,20 @@ #include #include -#include "infra/pipetrace.h" - enum ctlreg { DATA_INSTRUCTION_FIELD_BUFFER, // (df << 3) | if_buffer DATA_INSTRUCTION_FIELD_SAVED, // (df_saved << 3) | if_saved + HALTED, INT_ENABLE, // (int_enable_delay << 1) | int_enable + TT_INPUT, // (tti_flag << 8) | tti_buffer + TT_INPUT_INT_ENABLE, + TT_OUTPUT, // {tto_flag_old, tto_flag, tto_tx, tto_data} + TT_OUTPUT_INT_ENABLE, NUM_CTLREGS, }; struct instruction_context { - infra::transaction transaction; - // Known statically at decode time bool need_indirect_load = false; // final_address = mem[init_address] bool need_autoinc_store = false; // mem[init_address] += 1 @@ -43,7 +44,6 @@ struct instruction_context { std::optional acc; std::optional link; std::optional mq; - bool halt = false; }; -instruction_context decode(unsigned int df, unsigned int pc, unsigned int bits); +instruction_context decode(unsigned int df, unsigned int pc, unsigned int bits, bool interrupt); diff --git a/main.cpp b/main.cpp index 64d1a04..b587ce4 100644 --- a/main.cpp +++ b/main.cpp @@ -30,10 +30,8 @@ int load_program(checker &checker, const std::uint8_t *program) { address = ((b1 & 0077) << 6) | *program++; } else if ((b1 & 0300) == 0000) { seen_non_leader = true; - if (data.has_value()) { - //std::cout << fmt::format("mem[{:06o}] = {:04o}\n", data->first, data->second); + if (data.has_value()) checker.mem.store(data->first, data->second); - } auto a = field | address++; auto d = ((b1 & 0077) << 6) | *program++; data = std::make_pair(a, d); @@ -58,7 +56,8 @@ int main(int argc, const char *argv[]) { return 1; } - checker checker; + iomodel system; + checker checker(system); for (--argc, ++argv; argc; --argc, ++argv) { auto program = programs.at(argv[0]); @@ -66,10 +65,9 @@ int main(int argc, const char *argv[]) { return err; } - while (!checker.halted) { - std::cout << fmt::format("{:04o}: ", checker.pc); + while (true) { + std::cout << fmt::format("{:9} @{:04o} {:01o}:{:04o}:{:04o}\n", system.time, checker.pc, checker.link, checker.acc, checker.mq); checker.execute(); - std::cout << fmt::format("link={:01o} acc={:04o} mq={:04o}\n", checker.link, checker.acc, checker.mq); } return 0; -- cgit v1.2.3